2 void generic_plat_blocked()
4 if(self.dmg && other.takedamage != DAMAGE_NO) {
5 if(self.dmgtime2 < time) {
6 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
7 self.dmgtime2 = time + self.dmgtime;
10 // Gib dead/dying stuff
11 if(other.deadflag != DEAD_NO)
12 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
18 float STATE_BOTTOM = 1;
22 .entity trigger_field;
24 void() plat_center_touch;
25 void() plat_outside_touch;
26 void() plat_trigger_use;
30 float PLAT_LOW_TRIGGER = 1;
32 void plat_spawn_inside_trigger()
38 trigger.touch = plat_center_touch;
39 trigger.movetype = MOVETYPE_NONE;
40 trigger.solid = SOLID_TRIGGER;
43 tmin = self.absmin + '25 25 0';
44 tmax = self.absmax - '25 25 -8';
45 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
46 if (self.spawnflags & PLAT_LOW_TRIGGER)
49 if (self.size_x <= 50)
51 tmin_x = (self.mins_x + self.maxs_x) / 2;
54 if (self.size_y <= 50)
56 tmin_y = (self.mins_y + self.maxs_y) / 2;
64 setsize (trigger, tmin, tmax);
68 // otherwise, something is fishy...
70 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
75 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
77 self.think = plat_go_down;
78 self.nextthink = self.ltime + 3;
81 void plat_hit_bottom()
83 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
89 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
91 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
101 void plat_center_touch()
103 if not(other.iscreature)
106 if (other.health <= 0)
112 else if (self.state == 1)
113 self.nextthink = self.ltime + 1; // delay going down
116 void plat_outside_touch()
118 if not(other.iscreature)
121 if (other.health <= 0)
129 void plat_trigger_use()
132 return; // already activated
139 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
140 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
142 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
143 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
144 // Gib dead/dying stuff
145 if(other.deadflag != DEAD_NO)
146 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
151 else if (self.state == 3)
153 // when in other states, then the plat_crush event came delayed after
154 // plat state already had changed
155 // this isn't a bug per se!
163 objerror ("plat_use: not in up state");
167 .string sound1, sound2;
173 setorigin (self, self.pos1);
179 setorigin (self, self.pos2);
181 self.use = plat_trigger_use;
185 .float platmovetype_start_default, platmovetype_end_default;
186 float set_platmovetype(entity e, string s)
188 // sets platmovetype_start and platmovetype_end based on a string consisting of two values
191 n = tokenize_console(s);
193 e.platmovetype_start = stof(argv(0));
195 e.platmovetype_start = 0;
198 e.platmovetype_end = stof(argv(1));
200 e.platmovetype_end = e.platmovetype_start;
203 if(argv(2) == "force")
204 return TRUE; // no checking, return immediately
206 if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
208 objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
215 void spawnfunc_path_corner()
217 // setup values for overriding train movement
218 // if a second value does not exist, both start and end speeds are the single value specified
219 if(!set_platmovetype(self, self.platmovetype))
222 void spawnfunc_func_plat()
224 if (self.sounds == 0)
227 if(self.spawnflags & 4)
230 if(self.dmg && (!self.message))
231 self.message = "was squished";
232 if(self.dmg && (!self.message2))
233 self.message2 = "was squished by";
235 if (self.sounds == 1)
237 precache_sound ("plats/plat1.wav");
238 precache_sound ("plats/plat2.wav");
239 self.noise = "plats/plat1.wav";
240 self.noise1 = "plats/plat2.wav";
243 if (self.sounds == 2)
245 precache_sound ("plats/medplat1.wav");
246 precache_sound ("plats/medplat2.wav");
247 self.noise = "plats/medplat1.wav";
248 self.noise1 = "plats/medplat2.wav";
253 precache_sound (self.sound1);
254 self.noise = self.sound1;
258 precache_sound (self.sound2);
259 self.noise1 = self.sound2;
262 self.mangle = self.angles;
263 self.angles = '0 0 0';
265 self.classname = "plat";
266 if not(InitMovingBrushTrigger())
268 self.effects |= EF_LOWPRECISION;
269 setsize (self, self.mins , self.maxs);
271 self.blocked = plat_crush;
278 self.height = self.size_z - self.lip;
280 self.pos1 = self.origin;
281 self.pos2 = self.origin;
282 self.pos2_z = self.origin_z - self.height;
284 self.reset = plat_reset;
287 plat_spawn_inside_trigger (); // the "start moving" trigger
290 .float train_wait_turning;
301 // if turning is enabled, the train will turn toward the next point while waiting
302 if(self.wait >= 0 && self.bezier_turn && !self.train_wait_turning)
306 if(self.spawnflags & 1) // bezier curves movement
308 targ = find(world, targetname, self.target);
309 org = normalize(targ.origin);
310 SUB_CalcAngleMove(org, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
314 targ = find(world, targetname, self.target);
315 org = normalize(targ.origin);
316 SUB_CalcAngleMove(org, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
318 self.train_wait_turning = TRUE;
323 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
325 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
327 self.train_wait_turning = FALSE;
332 self.think = train_next;
333 self.nextthink = self.ltime + self.wait;
342 targ = find(world, targetname, self.target);
343 self.target = targ.target;
344 if (self.spawnflags & 1)
348 cp = find(world, targetname, targ.curve); // get its second target (the control point)
349 cp_org = cp.origin - self.mins; // no control point found, assume a straight line to the destination
355 objerror("train_next: no next target");
356 self.wait = targ.wait;
360 if(targ.platmovetype)
362 // this path_corner contains a movetype overrider, apply it
363 self.platmovetype_start = targ.platmovetype_start;
364 self.platmovetype_end = targ.platmovetype_end;
368 // this path_corner doesn't contain a movetype overrider, use the train's defaults
369 self.platmovetype_start = self.platmovetype_start_default;
370 self.platmovetype_end = self.platmovetype_end_default;
376 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
378 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
383 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
385 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
389 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
392 void func_train_find()
395 targ = find(world, targetname, self.target);
396 self.target = targ.target;
398 objerror("func_train_find: no next target");
399 setorigin(self, targ.origin - self.mins);
400 self.nextthink = self.ltime + 1;
401 self.think = train_next;
404 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
405 Ridable platform, targets spawnfunc_path_corner path to follow.
406 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
407 target : targetname of first spawnfunc_path_corner (starts here)
409 void spawnfunc_func_train()
411 if (self.noise != "")
412 precache_sound(self.noise);
415 objerror("func_train without a target");
418 if (self.spawnflags & 2)
419 self.bezier_turn = TRUE;
421 if not(InitMovingBrushTrigger())
423 self.effects |= EF_LOWPRECISION;
425 // wait for targets to spawn
426 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
428 self.blocked = generic_plat_blocked;
429 if(self.dmg & (!self.message))
430 self.message = " was squished";
431 if(self.dmg && (!self.message2))
432 self.message2 = "was squished by";
433 if(self.dmg && (!self.dmgtime))
435 self.dmgtime2 = time;
437 if(!set_platmovetype(self, self.platmovetype))
439 self.platmovetype_start_default = self.platmovetype_start;
440 self.platmovetype_end_default = self.platmovetype_end;
442 // TODO make a reset function for this one
445 void func_rotating_setactive(float astate)
448 if (astate == ACTIVE_TOGGLE)
450 if(self.active == ACTIVE_ACTIVE)
451 self.active = ACTIVE_NOT;
453 self.active = ACTIVE_ACTIVE;
456 self.active = astate;
458 if(self.active == ACTIVE_NOT)
459 self.avelocity = '0 0 0';
461 self.avelocity = self.pos1;
464 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
465 Brush model that spins in place on one axis (default Z).
466 speed : speed to rotate (in degrees per second)
467 noise : path/name of looping .wav file to play.
468 dmg : Do this mutch dmg every .dmgtime intervall when blocked
472 void spawnfunc_func_rotating()
474 if (self.noise != "")
476 precache_sound(self.noise);
477 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
480 self.active = ACTIVE_ACTIVE;
481 self.setactive = func_rotating_setactive;
485 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
486 if (self.spawnflags & 4) // X (untested)
487 self.avelocity = '0 0 1' * self.speed;
488 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
489 else if (self.spawnflags & 8) // Y (untested)
490 self.avelocity = '1 0 0' * self.speed;
491 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
493 self.avelocity = '0 1 0' * self.speed;
495 self.pos1 = self.avelocity;
497 if(self.dmg & (!self.message))
498 self.message = " was squished";
499 if(self.dmg && (!self.message2))
500 self.message2 = "was squished by";
503 if(self.dmg && (!self.dmgtime))
506 self.dmgtime2 = time;
508 if not(InitMovingBrushTrigger())
510 // no EF_LOWPRECISION here, as rounding angles is bad
512 self.blocked = generic_plat_blocked;
514 // wait for targets to spawn
515 self.nextthink = self.ltime + 999999999;
516 self.think = SUB_Null;
518 // TODO make a reset function for this one
522 void func_bobbing_controller_think()
525 self.nextthink = time + 0.1;
527 if not (self.owner.active == ACTIVE_ACTIVE)
529 self.owner.velocity = '0 0 0';
533 // calculate sinewave using makevectors
534 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
535 v = self.owner.destvec + self.owner.movedir * v_forward_y;
536 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
537 // * 10 so it will arrive in 0.1 sec
538 self.owner.velocity = (v - self.owner.origin) * 10;
541 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
542 Brush model that moves back and forth on one axis (default Z).
543 speed : how long one cycle takes in seconds (default 4)
544 height : how far the cycle moves (default 32)
545 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
546 noise : path/name of looping .wav file to play.
547 dmg : Do this mutch dmg every .dmgtime intervall when blocked
550 void spawnfunc_func_bobbing()
553 if (self.noise != "")
555 precache_sound(self.noise);
556 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
562 // center of bobbing motion
563 self.destvec = self.origin;
564 // time scale to get degrees
565 self.cnt = 360 / self.speed;
567 self.active = ACTIVE_ACTIVE;
569 // damage when blocked
570 self.blocked = generic_plat_blocked;
571 if(self.dmg & (!self.message))
572 self.message = " was squished";
573 if(self.dmg && (!self.message2))
574 self.message2 = "was squished by";
575 if(self.dmg && (!self.dmgtime))
577 self.dmgtime2 = time;
580 if (self.spawnflags & 1) // X
581 self.movedir = '1 0 0' * self.height;
582 else if (self.spawnflags & 2) // Y
583 self.movedir = '0 1 0' * self.height;
585 self.movedir = '0 0 1' * self.height;
587 if not(InitMovingBrushTrigger())
590 // wait for targets to spawn
591 controller = spawn();
592 controller.classname = "func_bobbing_controller";
593 controller.owner = self;
594 controller.nextthink = time + 1;
595 controller.think = func_bobbing_controller_think;
596 self.nextthink = self.ltime + 999999999;
597 self.think = SUB_Null;
599 // Savage: Reduce bandwith, critical on e.g. nexdm02
600 self.effects |= EF_LOWPRECISION;
602 // TODO make a reset function for this one
606 void func_pendulum_controller_think()
609 self.nextthink = time + 0.1;
611 if not (self.owner.active == ACTIVE_ACTIVE)
613 self.owner.avelocity_x = 0;
617 // calculate sinewave using makevectors
618 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
619 v = self.owner.speed * v_forward_y + self.cnt;
620 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
622 // * 10 so it will arrive in 0.1 sec
623 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
627 void spawnfunc_func_pendulum()
630 if (self.noise != "")
632 precache_sound(self.noise);
633 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
636 self.active = ACTIVE_ACTIVE;
638 // keys: angle, speed, phase, noise, freq
642 // not initializing self.dmg to 2, to allow damageless pendulum
644 if(self.dmg & (!self.message))
645 self.message = " was squished";
646 if(self.dmg && (!self.message2))
647 self.message2 = "was squished by";
648 if(self.dmg && (!self.dmgtime))
650 self.dmgtime2 = time;
652 self.blocked = generic_plat_blocked;
654 self.avelocity_z = 0.0000001;
655 if not(InitMovingBrushTrigger())
660 // find pendulum length (same formula as Q3A)
661 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
664 // copy initial angle
665 self.cnt = self.angles_z;
667 // wait for targets to spawn
668 controller = spawn();
669 controller.classname = "func_pendulum_controller";
670 controller.owner = self;
671 controller.nextthink = time + 1;
672 controller.think = func_pendulum_controller_think;
673 self.nextthink = self.ltime + 999999999;
674 self.think = SUB_Null;
676 //self.effects |= EF_LOWPRECISION;
678 // TODO make a reset function for this one
681 // button and multiple button
684 void() button_return;
688 self.state = STATE_TOP;
689 self.nextthink = self.ltime + self.wait;
690 self.think = button_return;
691 activator = self.enemy;
693 self.frame = 1; // use alternate textures
698 self.state = STATE_BOTTOM;
703 self.state = STATE_DOWN;
704 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
705 self.frame = 0; // use normal textures
707 self.takedamage = DAMAGE_YES; // can be shot again
711 void button_blocked()
713 // do nothing, just don't come all the way back out
719 self.health = self.max_health;
720 self.takedamage = DAMAGE_NO; // will be reset upon return
722 if (self.state == STATE_UP || self.state == STATE_TOP)
725 if (self.noise != "")
726 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
728 self.state = STATE_UP;
729 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
734 self.health = self.max_health;
735 setorigin(self, self.pos1);
736 self.frame = 0; // use normal textures
737 self.state = STATE_BOTTOM;
739 self.takedamage = DAMAGE_YES; // can be shot again
744 // if (activator.classname != "player")
746 // dprint(activator.classname);
747 // dprint(" triggered a button\n");
750 if not (self.active == ACTIVE_ACTIVE)
753 self.enemy = activator;
759 // if (activator.classname != "player")
761 // dprint(activator.classname);
762 // dprint(" touched a button\n");
766 if not(other.iscreature)
768 if(other.velocity * self.movedir < 0)
772 self.enemy = other.owner;
776 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
778 if(self.spawnflags & DOOR_NOSPLASH)
779 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
781 self.health = self.health - damage;
782 if (self.health <= 0)
784 // if (activator.classname != "player")
786 // dprint(activator.classname);
787 // dprint(" killed a button\n");
789 self.enemy = damage_attacker;
795 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
796 When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
798 "angle" determines the opening direction
799 "target" all entities with a matching targetname will be used
800 "speed" override the default 40 speed
801 "wait" override the default 1 second wait (-1 = never return)
802 "lip" override the default 4 pixel lip remaining at end of move
803 "health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the MinstaGib laser
810 void spawnfunc_func_button()
814 if not(InitMovingBrushTrigger())
816 self.effects |= EF_LOWPRECISION;
818 self.blocked = button_blocked;
819 self.use = button_use;
821 // if (self.health == 0) // all buttons are now shootable
825 self.max_health = self.health;
826 self.event_damage = button_damage;
827 self.takedamage = DAMAGE_YES;
830 self.touch = button_touch;
840 precache_sound(self.noise);
842 self.active = ACTIVE_ACTIVE;
844 self.pos1 = self.origin;
845 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
846 self.flags |= FL_NOTARGET;
852 float DOOR_START_OPEN = 1;
853 float DOOR_DONT_LINK = 4;
854 float DOOR_TOGGLE = 32;
858 Doors are similar to buttons, but can spawn a fat trigger field around them
859 to open without a touch, and they link together to form simultanious
862 Door.owner is the master door. If there is only one door, it points to itself.
863 If multiple doors, all will point to a single one.
865 Door.enemy chains from the master door through all doors linked in the chain.
870 =============================================================================
874 =============================================================================
879 void() door_rotating_go_down;
880 void() door_rotating_go_up;
885 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
886 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
889 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
890 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
892 //Dont chamge direction for dead or dying stuff
893 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
896 if (self.state == STATE_DOWN)
897 if (self.classname == "door")
902 door_rotating_go_up ();
905 if (self.classname == "door")
910 door_rotating_go_down ();
914 //gib dying stuff just to make sure
915 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
916 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
920 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
921 // if a door has a negative wait, it would never come back if blocked,
922 // so let it just squash the object to death real fast
923 /* if (self.wait >= 0)
925 if (self.state == STATE_DOWN)
936 if (self.noise1 != "")
937 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
938 self.state = STATE_TOP;
939 if (self.spawnflags & DOOR_TOGGLE)
940 return; // don't come down automatically
941 if (self.classname == "door")
943 self.think = door_go_down;
946 self.think = door_rotating_go_down;
948 self.nextthink = self.ltime + self.wait;
951 void door_hit_bottom()
953 if (self.noise1 != "")
954 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
955 self.state = STATE_BOTTOM;
960 if (self.noise2 != "")
961 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
964 self.takedamage = DAMAGE_YES;
965 self.health = self.max_health;
968 self.state = STATE_DOWN;
969 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
974 if (self.state == STATE_UP)
975 return; // already going up
977 if (self.state == STATE_TOP)
978 { // reset top wait time
979 self.nextthink = self.ltime + self.wait;
983 if (self.noise2 != "")
984 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
985 self.state = STATE_UP;
986 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
989 oldmessage = self.message;
992 self.message = oldmessage;
998 =============================================================================
1000 ACTIVATION FUNCTIONS
1002 =============================================================================
1005 float door_check_keys(void) {
1015 if not(door.itemkeys)
1018 // this door require a key
1019 // only a player can have a key
1020 if (other.classname != "player")
1023 if (item_keys_usekey(door, other)) {
1024 // some keys were used
1025 if (other.key_door_messagetime <= time) {
1026 play2(other, "misc/talk.wav");
1027 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
1028 other.key_door_messagetime = time + 2;
1031 // no keys were used
1032 if (other.key_door_messagetime <= time) {
1033 play2(other, "misc/talk.wav");
1034 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
1035 other.key_door_messagetime = time + 2;
1039 if (door.itemkeys) {
1040 // door is now unlocked
1041 play2(other, "misc/talk.wav");
1042 centerprint(other, "Door unlocked!");
1054 if (self.owner != self)
1055 objerror ("door_fire: self.owner != self");
1059 if (self.spawnflags & DOOR_TOGGLE)
1061 if (self.state == STATE_UP || self.state == STATE_TOP)
1066 if (self.classname == "door")
1072 door_rotating_go_down ();
1075 } while ( (self != starte) && (self != world) );
1081 // trigger all paired doors
1085 if (self.classname == "door")
1090 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1091 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1093 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1094 self.pos2 = '0 0 0' - self.pos2;
1096 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1097 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1098 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1100 door_rotating_go_up ();
1104 } while ( (self != starte) && (self != world) );
1113 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1125 void door_trigger_touch()
1127 if (other.health < 1)
1128 if not(other.iscreature && other.deadflag == DEAD_NO)
1131 if (time < self.attack_finished_single)
1134 // check if door is locked
1135 if (!door_check_keys())
1138 self.attack_finished_single = time + 1;
1147 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1150 if(self.spawnflags & DOOR_NOSPLASH)
1151 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1153 self.health = self.health - damage;
1155 if (self.itemkeys) {
1156 // don't allow opening doors through damage if keys are required
1160 if (self.health <= 0)
1164 self.health = self.max_health;
1165 self.takedamage = DAMAGE_NO; // wil be reset upon return
1181 if(other.classname != "player")
1183 if (self.owner.attack_finished_single > time)
1186 self.owner.attack_finished_single = time + 2;
1188 if (!(self.owner.dmg) && (self.owner.message != ""))
1190 if (other.flags & FL_CLIENT)
1191 centerprint (other, self.owner.message);
1192 play2(other, "misc/talk.wav");
1197 void door_generic_plat_blocked()
1200 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1201 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1204 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1205 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1207 //Dont chamge direction for dead or dying stuff
1208 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1211 if (self.state == STATE_DOWN)
1212 door_rotating_go_up ();
1214 door_rotating_go_down ();
1217 //gib dying stuff just to make sure
1218 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1219 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1223 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1224 // if a door has a negative wait, it would never come back if blocked,
1225 // so let it just squash the object to death real fast
1226 /* if (self.wait >= 0)
1228 if (self.state == STATE_DOWN)
1229 door_rotating_go_up ();
1231 door_rotating_go_down ();
1237 void door_rotating_hit_top()
1239 if (self.noise1 != "")
1240 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1241 self.state = STATE_TOP;
1242 if (self.spawnflags & DOOR_TOGGLE)
1243 return; // don't come down automatically
1244 self.think = door_rotating_go_down;
1245 self.nextthink = self.ltime + self.wait;
1248 void door_rotating_hit_bottom()
1250 if (self.noise1 != "")
1251 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1252 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1254 self.pos2 = '0 0 0' - self.pos2;
1257 self.state = STATE_BOTTOM;
1260 void door_rotating_go_down()
1262 if (self.noise2 != "")
1263 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1264 if (self.max_health)
1266 self.takedamage = DAMAGE_YES;
1267 self.health = self.max_health;
1270 self.state = STATE_DOWN;
1271 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1274 void door_rotating_go_up()
1276 if (self.state == STATE_UP)
1277 return; // already going up
1279 if (self.state == STATE_TOP)
1280 { // reset top wait time
1281 self.nextthink = self.ltime + self.wait;
1284 if (self.noise2 != "")
1285 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1286 self.state = STATE_UP;
1287 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1290 oldmessage = self.message;
1293 self.message = oldmessage;
1300 =============================================================================
1304 =============================================================================
1308 entity spawn_field(vector fmins, vector fmaxs)
1314 trigger.classname = "doortriggerfield";
1315 trigger.movetype = MOVETYPE_NONE;
1316 trigger.solid = SOLID_TRIGGER;
1317 trigger.owner = self;
1318 trigger.touch = door_trigger_touch;
1322 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1327 float EntitiesTouching(entity e1, entity e2)
1329 if (e1.absmin_x > e2.absmax_x)
1331 if (e1.absmin_y > e2.absmax_y)
1333 if (e1.absmin_z > e2.absmax_z)
1335 if (e1.absmax_x < e2.absmin_x)
1337 if (e1.absmax_y < e2.absmin_y)
1339 if (e1.absmax_z < e2.absmin_z)
1355 vector cmins, cmaxs;
1358 return; // already linked by another door
1359 if (self.spawnflags & 4)
1361 self.owner = self.enemy = self;
1369 self.trigger_field = spawn_field(self.absmin, self.absmax);
1371 return; // don't want to link this door
1374 cmins = self.absmin;
1375 cmaxs = self.absmax;
1382 self.owner = starte; // master door
1385 starte.health = self.health;
1387 starte.targetname = self.targetname;
1388 if (self.message != "")
1389 starte.message = self.message;
1391 t = find(t, classname, self.classname);
1394 self.enemy = starte; // make the chain a loop
1396 // shootable, or triggered doors just needed the owner/enemy links,
1397 // they don't spawn a field
1408 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1413 if (EntitiesTouching(self,t))
1416 objerror ("cross connected doors");
1421 if (t.absmin_x < cmins_x)
1422 cmins_x = t.absmin_x;
1423 if (t.absmin_y < cmins_y)
1424 cmins_y = t.absmin_y;
1425 if (t.absmin_z < cmins_z)
1426 cmins_z = t.absmin_z;
1427 if (t.absmax_x > cmaxs_x)
1428 cmaxs_x = t.absmax_x;
1429 if (t.absmax_y > cmaxs_y)
1430 cmaxs_y = t.absmax_y;
1431 if (t.absmax_z > cmaxs_z)
1432 cmaxs_z = t.absmax_z;
1439 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1440 if two doors touch, they are assumed to be connected and operate as a unit.
1442 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1444 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
1446 GOLD_KEY causes the door to open only if the activator holds a gold key.
1448 SILVER_KEY causes the door to open only if the activator holds a silver key.
1450 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1451 "angle" determines the opening direction
1452 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1453 "health" if set, door must be shot open
1454 "speed" movement speed (100 default)
1455 "wait" wait before returning (3 default, -1 = never return)
1456 "lip" lip remaining at end of move (8 default)
1457 "dmg" damage to inflict when blocked (2 default)
1464 FIXME: only one sound set available at the time being
1468 void door_init_startopen()
1470 setorigin (self, self.pos2);
1471 self.pos2 = self.pos1;
1472 self.pos1 = self.origin;
1477 setorigin(self, self.pos1);
1478 self.velocity = '0 0 0';
1479 self.state = STATE_BOTTOM;
1480 self.think = SUB_Null;
1483 // spawnflags require key (for now only func_door)
1484 #define SPAWNFLAGS_GOLD_KEY 8
1485 #define SPAWNFLAGS_SILVER_KEY 16
1486 void spawnfunc_func_door()
1488 // Quake 1 keys compatibility
1489 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1490 self.itemkeys |= ITEM_KEY_BIT(0);
1491 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1492 self.itemkeys |= ITEM_KEY_BIT(1);
1494 //if (!self.deathtype) // map makers can override this
1495 // self.deathtype = " got in the way";
1498 self.max_health = self.health;
1499 if not(InitMovingBrushTrigger())
1501 self.effects |= EF_LOWPRECISION;
1502 self.classname = "door";
1504 self.blocked = door_blocked;
1505 self.use = door_use;
1507 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1508 // if(self.spawnflags & 8)
1509 // self.dmg = 10000;
1511 if(self.dmg && (!self.message))
1512 self.message = "was squished";
1513 if(self.dmg && (!self.message2))
1514 self.message2 = "was squished by";
1516 if (self.sounds > 0)
1518 precache_sound ("plats/medplat1.wav");
1519 precache_sound ("plats/medplat2.wav");
1520 self.noise2 = "plats/medplat1.wav";
1521 self.noise1 = "plats/medplat2.wav";
1531 self.pos1 = self.origin;
1532 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1534 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1535 // but spawn in the open position
1536 if (self.spawnflags & DOOR_START_OPEN)
1537 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1539 self.state = STATE_BOTTOM;
1543 self.takedamage = DAMAGE_YES;
1544 self.event_damage = door_damage;
1550 self.touch = door_touch;
1552 // LinkDoors can't be done until all of the doors have been spawned, so
1553 // the sizes can be detected properly.
1554 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1556 self.reset = door_reset;
1559 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1560 if two doors touch, they are assumed to be connected and operate as a unit.
1562 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1564 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1565 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1566 must have set trigger_reverse to 1.
1567 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1569 START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
1571 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1572 "angle" determines the destination angle for opening. negative values reverse the direction.
1573 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1574 "health" if set, door must be shot open
1575 "speed" movement speed (100 default)
1576 "wait" wait before returning (3 default, -1 = never return)
1577 "dmg" damage to inflict when blocked (2 default)
1584 FIXME: only one sound set available at the time being
1587 void door_rotating_reset()
1589 self.angles = self.pos1;
1590 self.avelocity = '0 0 0';
1591 self.state = STATE_BOTTOM;
1592 self.think = SUB_Null;
1595 void door_rotating_init_startopen()
1597 self.angles = self.movedir;
1598 self.pos2 = '0 0 0';
1599 self.pos1 = self.movedir;
1603 void spawnfunc_func_door_rotating()
1606 //if (!self.deathtype) // map makers can override this
1607 // self.deathtype = " got in the way";
1609 // I abuse "movedir" for denoting the axis for now
1610 if (self.spawnflags & 64) // X (untested)
1611 self.movedir = '0 0 1';
1612 else if (self.spawnflags & 128) // Y (untested)
1613 self.movedir = '1 0 0';
1615 self.movedir = '0 1 0';
1617 if (self.angles_y==0) self.angles_y = 90;
1619 self.movedir = self.movedir * self.angles_y;
1620 self.angles = '0 0 0';
1622 self.max_health = self.health;
1623 self.avelocity = self.movedir;
1624 if not(InitMovingBrushTrigger())
1626 self.velocity = '0 0 0';
1627 //self.effects |= EF_LOWPRECISION;
1628 self.classname = "door_rotating";
1630 self.blocked = door_blocked;
1631 self.use = door_use;
1633 if(self.spawnflags & 8)
1636 if(self.dmg && (!self.message))
1637 self.message = "was squished";
1638 if(self.dmg && (!self.message2))
1639 self.message2 = "was squished by";
1641 if (self.sounds > 0)
1643 precache_sound ("plats/medplat1.wav");
1644 precache_sound ("plats/medplat2.wav");
1645 self.noise2 = "plats/medplat1.wav";
1646 self.noise1 = "plats/medplat2.wav";
1653 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1655 self.pos1 = '0 0 0';
1656 self.pos2 = self.movedir;
1658 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1659 // but spawn in the open position
1660 if (self.spawnflags & DOOR_START_OPEN)
1661 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1663 self.state = STATE_BOTTOM;
1667 self.takedamage = DAMAGE_YES;
1668 self.event_damage = door_damage;
1674 self.touch = door_touch;
1676 // LinkDoors can't be done until all of the doors have been spawned, so
1677 // the sizes can be detected properly.
1678 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1680 self.reset = door_rotating_reset;
1684 =============================================================================
1688 =============================================================================
1691 void() fd_secret_move1;
1692 void() fd_secret_move2;
1693 void() fd_secret_move3;
1694 void() fd_secret_move4;
1695 void() fd_secret_move5;
1696 void() fd_secret_move6;
1697 void() fd_secret_done;
1699 float SECRET_OPEN_ONCE = 1; // stays open
1700 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1701 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1702 float SECRET_NO_SHOOT = 8; // only opened by trigger
1703 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1706 void fd_secret_use()
1709 string message_save;
1711 self.health = 10000;
1712 self.bot_attack = TRUE;
1714 // exit if still moving around...
1715 if (self.origin != self.oldorigin)
1718 message_save = self.message;
1719 self.message = ""; // no more message
1720 SUB_UseTargets(); // fire all targets / killtargets
1721 self.message = message_save;
1723 self.velocity = '0 0 0';
1725 // Make a sound, wait a little...
1727 if (self.noise1 != "")
1728 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1729 self.nextthink = self.ltime + 0.1;
1731 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1732 makevectors(self.mangle);
1736 if (self.spawnflags & SECRET_1ST_DOWN)
1737 self.t_width = fabs(v_up * self.size);
1739 self.t_width = fabs(v_right * self.size);
1743 self.t_length = fabs(v_forward * self.size);
1745 if (self.spawnflags & SECRET_1ST_DOWN)
1746 self.dest1 = self.origin - v_up * self.t_width;
1748 self.dest1 = self.origin + v_right * (self.t_width * temp);
1750 self.dest2 = self.dest1 + v_forward * self.t_length;
1751 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1752 if (self.noise2 != "")
1753 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1756 // Wait after first movement...
1757 void fd_secret_move1()
1759 self.nextthink = self.ltime + 1.0;
1760 self.think = fd_secret_move2;
1761 if (self.noise3 != "")
1762 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1765 // Start moving sideways w/sound...
1766 void fd_secret_move2()
1768 if (self.noise2 != "")
1769 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1770 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1773 // Wait here until time to go back...
1774 void fd_secret_move3()
1776 if (self.noise3 != "")
1777 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1778 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1780 self.nextthink = self.ltime + self.wait;
1781 self.think = fd_secret_move4;
1786 void fd_secret_move4()
1788 if (self.noise2 != "")
1789 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1790 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1794 void fd_secret_move5()
1796 self.nextthink = self.ltime + 1.0;
1797 self.think = fd_secret_move6;
1798 if (self.noise3 != "")
1799 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1802 void fd_secret_move6()
1804 if (self.noise2 != "")
1805 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1806 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1809 void fd_secret_done()
1811 if (self.spawnflags&SECRET_YES_SHOOT)
1813 self.health = 10000;
1814 self.takedamage = DAMAGE_YES;
1815 //self.th_pain = fd_secret_use;
1817 if (self.noise3 != "")
1818 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1821 void secret_blocked()
1823 if (time < self.attack_finished_single)
1825 self.attack_finished_single = time + 0.5;
1826 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1838 if not(other.iscreature)
1840 if (self.attack_finished_single > time)
1843 self.attack_finished_single = time + 2;
1847 if (other.flags & FL_CLIENT)
1848 centerprint (other, self.message);
1849 play2(other, "misc/talk.wav");
1855 if (self.spawnflags&SECRET_YES_SHOOT)
1857 self.health = 10000;
1858 self.takedamage = DAMAGE_YES;
1860 setorigin(self, self.oldorigin);
1861 self.think = SUB_Null;
1864 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1865 Basic secret door. Slides back, then to the side. Angle determines direction.
1866 wait = # of seconds before coming back
1867 1st_left = 1st move is left of arrow
1868 1st_down = 1st move is down from arrow
1869 always_shoot = even if targeted, keep shootable
1870 t_width = override WIDTH to move back (or height if going down)
1871 t_length = override LENGTH to move sideways
1872 "dmg" damage to inflict when blocked (2 default)
1874 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1881 void spawnfunc_func_door_secret()
1883 /*if (!self.deathtype) // map makers can override this
1884 self.deathtype = " got in the way";*/
1890 self.mangle = self.angles;
1891 self.angles = '0 0 0';
1892 self.classname = "door";
1893 if not(InitMovingBrushTrigger())
1895 self.effects |= EF_LOWPRECISION;
1897 self.touch = secret_touch;
1898 self.blocked = secret_blocked;
1900 self.use = fd_secret_use;
1905 self.spawnflags |= SECRET_YES_SHOOT;
1907 if(self.spawnflags&SECRET_YES_SHOOT)
1909 self.health = 10000;
1910 self.takedamage = DAMAGE_YES;
1911 self.event_damage = fd_secret_use;
1913 self.oldorigin = self.origin;
1915 self.wait = 5; // 5 seconds before closing
1917 self.reset = secret_reset;
1921 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1922 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1923 netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
1924 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1925 height: amplitude modifier (default 32)
1926 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1927 noise: path/name of looping .wav file to play.
1928 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1932 void func_fourier_controller_think()
1937 self.nextthink = time + 0.1;
1938 if not (self.owner.active == ACTIVE_ACTIVE)
1940 self.owner.velocity = '0 0 0';
1945 n = floor((tokenize_console(self.owner.netname)) / 5);
1946 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1948 v = self.owner.destvec;
1950 for(i = 0; i < n; ++i)
1952 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1953 v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward_y;
1956 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1957 // * 10 so it will arrive in 0.1 sec
1958 self.owner.velocity = (v - self.owner.origin) * 10;
1961 void spawnfunc_func_fourier()
1964 if (self.noise != "")
1966 precache_sound(self.noise);
1967 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1974 self.destvec = self.origin;
1975 self.cnt = 360 / self.speed;
1977 self.blocked = generic_plat_blocked;
1978 if(self.dmg & (!self.message))
1979 self.message = " was squished";
1980 if(self.dmg && (!self.message2))
1981 self.message2 = "was squished by";
1982 if(self.dmg && (!self.dmgtime))
1983 self.dmgtime = 0.25;
1984 self.dmgtime2 = time;
1986 if(self.netname == "")
1987 self.netname = "1 0 0 0 1";
1989 if not(InitMovingBrushTrigger())
1992 self.active = ACTIVE_ACTIVE;
1994 // wait for targets to spawn
1995 controller = spawn();
1996 controller.classname = "func_fourier_controller";
1997 controller.owner = self;
1998 controller.nextthink = time + 1;
1999 controller.think = func_fourier_controller_think;
2000 self.nextthink = self.ltime + 999999999;
2001 self.think = SUB_Null;
2003 // Savage: Reduce bandwith, critical on e.g. nexdm02
2004 self.effects |= EF_LOWPRECISION;
2006 // TODO make a reset function for this one
2009 // reusing some fields havocbots declared
2010 .entity wp00, wp01, wp02, wp03;
2012 .float targetfactor, target2factor, target3factor, target4factor;
2013 .vector targetnormal, target2normal, target3normal, target4normal;
2015 vector func_vectormamamam_origin(entity o, float t)
2027 p = e.origin + t * e.velocity;
2029 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
2031 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
2037 p = e.origin + t * e.velocity;
2039 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
2041 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
2047 p = e.origin + t * e.velocity;
2049 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
2051 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
2057 p = e.origin + t * e.velocity;
2059 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
2061 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2067 void func_vectormamamam_controller_think()
2069 self.nextthink = time + 0.1;
2071 if not (self.owner.active == ACTIVE_ACTIVE)
2073 self.owner.velocity = '0 0 0';
2077 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2078 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2081 void func_vectormamamam_findtarget()
2083 if(self.target != "")
2084 self.wp00 = find(world, targetname, self.target);
2086 if(self.target2 != "")
2087 self.wp01 = find(world, targetname, self.target2);
2089 if(self.target3 != "")
2090 self.wp02 = find(world, targetname, self.target3);
2092 if(self.target4 != "")
2093 self.wp03 = find(world, targetname, self.target4);
2095 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2096 objerror("No reference entity found, so there is nothing to move. Aborting.");
2098 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2101 controller = spawn();
2102 controller.classname = "func_vectormamamam_controller";
2103 controller.owner = self;
2104 controller.nextthink = time + 1;
2105 controller.think = func_vectormamamam_controller_think;
2108 void spawnfunc_func_vectormamamam()
2110 if (self.noise != "")
2112 precache_sound(self.noise);
2113 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2116 if(!self.targetfactor)
2117 self.targetfactor = 1;
2119 if(!self.target2factor)
2120 self.target2factor = 1;
2122 if(!self.target3factor)
2123 self.target3factor = 1;
2125 if(!self.target4factor)
2126 self.target4factor = 1;
2128 if(vlen(self.targetnormal))
2129 self.targetnormal = normalize(self.targetnormal);
2131 if(vlen(self.target2normal))
2132 self.target2normal = normalize(self.target2normal);
2134 if(vlen(self.target3normal))
2135 self.target3normal = normalize(self.target3normal);
2137 if(vlen(self.target4normal))
2138 self.target4normal = normalize(self.target4normal);
2140 self.blocked = generic_plat_blocked;
2141 if(self.dmg & (!self.message))
2142 self.message = " was squished";
2143 if(self.dmg && (!self.message2))
2144 self.message2 = "was squished by";
2145 if(self.dmg && (!self.dmgtime))
2146 self.dmgtime = 0.25;
2147 self.dmgtime2 = time;
2149 if(self.netname == "")
2150 self.netname = "1 0 0 0 1";
2152 if not(InitMovingBrushTrigger())
2155 // wait for targets to spawn
2156 self.nextthink = self.ltime + 999999999;
2157 self.think = SUB_Null;
2159 // Savage: Reduce bandwith, critical on e.g. nexdm02
2160 self.effects |= EF_LOWPRECISION;
2162 self.active = ACTIVE_ACTIVE;
2164 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2167 void conveyor_think()
2171 // set myself as current conveyor where possible
2172 for(e = world; (e = findentity(e, conveyor, self)); )
2177 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2178 if(!e.conveyor.state)
2181 vector emin = e.absmin;
2182 vector emax = e.absmax;
2183 if(self.solid == SOLID_BSP)
2188 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2189 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2193 for(e = world; (e = findentity(e, conveyor, self)); )
2195 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2196 continue; // done in SV_PlayerPhysics
2198 setorigin(e, e.origin + self.movedir * sys_frametime);
2199 move_out_of_solid(e);
2200 UpdateCSQCProjectile(e);
2202 // stupid conveyor code
2203 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2204 if(trace_fraction > 0)
2205 setorigin(e, trace_endpos);
2210 self.nextthink = time;
2215 self.state = !self.state;
2218 void conveyor_reset()
2220 self.state = (self.spawnflags & 1);
2223 void conveyor_init()
2227 self.movedir = self.movedir * self.speed;
2228 self.think = conveyor_think;
2229 self.nextthink = time;
2232 self.use = conveyor_use;
2233 self.reset = conveyor_reset;
2240 void spawnfunc_trigger_conveyor()
2247 void spawnfunc_func_conveyor()
2250 InitMovingBrushTrigger();
2251 self.movetype = MOVETYPE_NONE;