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()
35 local vector tmin, tmax;
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;
60 setsize (trigger, tmin, tmax);
65 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
67 self.think = plat_go_down;
68 self.nextthink = self.ltime + 3;
71 void plat_hit_bottom()
73 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
79 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
81 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
86 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
88 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
91 void plat_center_touch()
93 if not(other.iscreature)
96 if (other.health <= 0)
102 else if (self.state == 1)
103 self.nextthink = self.ltime + 1; // delay going down
106 void plat_outside_touch()
108 if not(other.iscreature)
111 if (other.health <= 0)
119 void plat_trigger_use()
122 return; // already activated
129 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
130 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
132 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
133 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
134 // Gib dead/dying stuff
135 if(other.deadflag != DEAD_NO)
136 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
141 else if (self.state == 3)
143 // when in other states, then the plat_crush event came delayed after
144 // plat state already had changed
145 // this isn't a bug per se!
153 objerror ("plat_use: not in up state");
157 .string sound1, sound2;
163 setorigin (self, self.pos1);
169 setorigin (self, self.pos2);
171 self.use = plat_trigger_use;
175 void spawnfunc_path_corner() { };
176 void spawnfunc_func_plat()
183 if (self.sounds == 0)
186 if(self.spawnflags & 4)
189 if(self.dmg && (!self.message))
190 self.message = "was squished";
191 if(self.dmg && (!self.message2))
192 self.message2 = "was squished by";
194 if (self.sounds == 1)
196 precache_sound ("plats/plat1.wav");
197 precache_sound ("plats/plat2.wav");
198 self.noise = "plats/plat1.wav";
199 self.noise1 = "plats/plat2.wav";
202 if (self.sounds == 2)
204 precache_sound ("plats/medplat1.wav");
205 precache_sound ("plats/medplat2.wav");
206 self.noise = "plats/medplat1.wav";
207 self.noise1 = "plats/medplat2.wav";
212 precache_sound (self.sound1);
213 self.noise = self.sound1;
217 precache_sound (self.sound2);
218 self.noise1 = self.sound2;
221 self.mangle = self.angles;
222 self.angles = '0 0 0';
224 self.classname = "plat";
225 if not(InitMovingBrushTrigger())
227 self.effects |= EF_LOWPRECISION;
228 setsize (self, self.mins , self.maxs);
230 self.blocked = plat_crush;
235 self.pos1 = self.origin;
236 self.pos2 = self.origin;
237 self.pos2_z = self.origin_z - self.size_z + 8;
239 plat_spawn_inside_trigger (); // the "start moving" trigger
241 self.reset = plat_reset;
250 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
258 self.think = train_next;
259 self.nextthink = self.ltime + self.wait;
273 targ = find(world, targetname, self.target);
275 self.target = targ.target;
277 objerror("train_next: no next target");
278 self.wait = targ.wait;
283 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
285 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
288 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
291 void func_train_find()
294 targ = find(world, targetname, self.target);
295 self.target = targ.target;
297 objerror("func_train_find: no next target");
298 setorigin(self, targ.origin - self.mins);
299 self.nextthink = self.ltime + 1;
300 self.think = train_next;
303 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
304 Ridable platform, targets spawnfunc_path_corner path to follow.
305 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
306 target : targetname of first spawnfunc_path_corner (starts here)
308 void spawnfunc_func_train()
310 if (self.noise != "")
311 precache_sound(self.noise);
314 objerror("func_train without a target");
318 if not(InitMovingBrushTrigger())
320 self.effects |= EF_LOWPRECISION;
322 // wait for targets to spawn
323 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
325 self.blocked = generic_plat_blocked;
326 if(self.dmg & (!self.message))
327 self.message = " was squished";
328 if(self.dmg && (!self.message2))
329 self.message2 = "was squished by";
330 if(self.dmg && (!self.dmgtime))
332 self.dmgtime2 = time;
334 // TODO make a reset function for this one
337 void func_rotating_setactive(float astate)
340 if (astate == ACTIVE_TOGGLE)
342 if(self.active == ACTIVE_ACTIVE)
343 self.active = ACTIVE_NOT;
345 self.active = ACTIVE_ACTIVE;
348 self.active = astate;
350 if(self.active == ACTIVE_NOT)
351 self.avelocity = '0 0 0';
353 self.avelocity = self.pos1;
356 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
357 Brush model that spins in place on one axis (default Z).
358 speed : speed to rotate (in degrees per second)
359 noise : path/name of looping .wav file to play.
360 dmg : Do this mutch dmg every .dmgtime intervall when blocked
364 void spawnfunc_func_rotating()
366 if (self.noise != "")
368 precache_sound(self.noise);
369 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
372 self.active = ACTIVE_ACTIVE;
373 self.setactive = func_rotating_setactive;
377 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
378 if (self.spawnflags & 4) // X (untested)
379 self.avelocity = '0 0 1' * self.speed;
380 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
381 else if (self.spawnflags & 8) // Y (untested)
382 self.avelocity = '1 0 0' * self.speed;
383 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
385 self.avelocity = '0 1 0' * self.speed;
387 self.pos1 = self.avelocity;
389 if(self.dmg & (!self.message))
390 self.message = " was squished";
391 if(self.dmg && (!self.message2))
392 self.message2 = "was squished by";
395 if(self.dmg && (!self.dmgtime))
398 self.dmgtime2 = time;
400 if not(InitMovingBrushTrigger())
402 // no EF_LOWPRECISION here, as rounding angles is bad
404 self.blocked = generic_plat_blocked;
406 // wait for targets to spawn
407 self.nextthink = self.ltime + 999999999;
408 self.think = SUB_Null;
410 // TODO make a reset function for this one
414 void func_bobbing_controller_think()
417 self.nextthink = time + 0.1;
419 if not (self.owner.active == ACTIVE_ACTIVE)
421 self.owner.velocity = '0 0 0';
425 // calculate sinewave using makevectors
426 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
427 v = self.owner.destvec + self.owner.movedir * v_forward_y;
428 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
429 // * 10 so it will arrive in 0.1 sec
430 self.owner.velocity = (v - self.owner.origin) * 10;
433 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
434 Brush model that moves back and forth on one axis (default Z).
435 speed : how long one cycle takes in seconds (default 4)
436 height : how far the cycle moves (default 32)
437 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
438 noise : path/name of looping .wav file to play.
439 dmg : Do this mutch dmg every .dmgtime intervall when blocked
442 void spawnfunc_func_bobbing()
444 local entity controller;
445 if (self.noise != "")
447 precache_sound(self.noise);
448 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
454 // center of bobbing motion
455 self.destvec = self.origin;
456 // time scale to get degrees
457 self.cnt = 360 / self.speed;
459 self.active = ACTIVE_ACTIVE;
461 // damage when blocked
462 self.blocked = generic_plat_blocked;
463 if(self.dmg & (!self.message))
464 self.message = " was squished";
465 if(self.dmg && (!self.message2))
466 self.message2 = "was squished by";
467 if(self.dmg && (!self.dmgtime))
469 self.dmgtime2 = time;
472 if (self.spawnflags & 1) // X
473 self.movedir = '1 0 0' * self.height;
474 else if (self.spawnflags & 2) // Y
475 self.movedir = '0 1 0' * self.height;
477 self.movedir = '0 0 1' * self.height;
479 if not(InitMovingBrushTrigger())
482 // wait for targets to spawn
483 controller = spawn();
484 controller.classname = "func_bobbing_controller";
485 controller.owner = self;
486 controller.nextthink = time + 1;
487 controller.think = func_bobbing_controller_think;
488 self.nextthink = self.ltime + 999999999;
489 self.think = SUB_Null;
491 // Savage: Reduce bandwith, critical on e.g. nexdm02
492 self.effects |= EF_LOWPRECISION;
494 // TODO make a reset function for this one
498 void func_pendulum_controller_think()
501 self.nextthink = time + 0.1;
503 if not (self.owner.active == ACTIVE_ACTIVE)
505 self.owner.avelocity_x = 0;
509 // calculate sinewave using makevectors
510 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
511 v = self.owner.speed * v_forward_y + self.cnt;
512 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
514 // * 10 so it will arrive in 0.1 sec
515 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
519 void spawnfunc_func_pendulum()
521 local entity controller;
522 if (self.noise != "")
524 precache_sound(self.noise);
525 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
528 self.active = ACTIVE_ACTIVE;
530 // keys: angle, speed, phase, noise, freq
534 // not initializing self.dmg to 2, to allow damageless pendulum
536 if(self.dmg & (!self.message))
537 self.message = " was squished";
538 if(self.dmg && (!self.message2))
539 self.message2 = "was squished by";
540 if(self.dmg && (!self.dmgtime))
542 self.dmgtime2 = time;
544 self.blocked = generic_plat_blocked;
546 self.avelocity_z = 0.0000001;
547 if not(InitMovingBrushTrigger())
552 // find pendulum length (same formula as Q3A)
553 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
556 // copy initial angle
557 self.cnt = self.angles_z;
559 // wait for targets to spawn
560 controller = spawn();
561 controller.classname = "func_pendulum_controller";
562 controller.owner = self;
563 controller.nextthink = time + 1;
564 controller.think = func_pendulum_controller_think;
565 self.nextthink = self.ltime + 999999999;
566 self.think = SUB_Null;
568 //self.effects |= EF_LOWPRECISION;
570 // TODO make a reset function for this one
573 // button and multiple button
576 void() button_return;
580 self.state = STATE_TOP;
581 self.nextthink = self.ltime + self.wait;
582 self.think = button_return;
583 activator = self.enemy;
585 self.frame = 1; // use alternate textures
590 self.state = STATE_BOTTOM;
595 self.state = STATE_DOWN;
596 SUB_CalcMove (self.pos1, self.speed, button_done);
597 self.frame = 0; // use normal textures
599 self.takedamage = DAMAGE_YES; // can be shot again
603 void button_blocked()
605 // do nothing, just don't come all the way back out
611 self.health = self.max_health;
612 self.takedamage = DAMAGE_NO; // will be reset upon return
614 if (self.state == STATE_UP || self.state == STATE_TOP)
617 if (self.noise != "")
618 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
620 self.state = STATE_UP;
621 SUB_CalcMove (self.pos2, self.speed, button_wait);
626 self.health = self.max_health;
627 setorigin(self, self.pos1);
628 self.frame = 0; // use normal textures
629 self.state = STATE_BOTTOM;
631 self.takedamage = DAMAGE_YES; // can be shot again
636 // if (activator.classname != "player")
638 // dprint(activator.classname);
639 // dprint(" triggered a button\n");
642 if not (self.active == ACTIVE_ACTIVE)
645 self.enemy = activator;
651 // if (activator.classname != "player")
653 // dprint(activator.classname);
654 // dprint(" touched a button\n");
658 if not(other.iscreature)
660 if(other.velocity * self.movedir < 0)
664 self.enemy = other.owner;
668 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
670 if(self.spawnflags & DOOR_NOSPLASH)
671 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
673 self.health = self.health - damage;
674 if (self.health <= 0)
676 // if (activator.classname != "player")
678 // dprint(activator.classname);
679 // dprint(" killed a button\n");
681 self.enemy = damage_attacker;
687 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
688 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.
690 "angle" determines the opening direction
691 "target" all entities with a matching targetname will be used
692 "speed" override the default 40 speed
693 "wait" override the default 1 second wait (-1 = never return)
694 "lip" override the default 4 pixel lip remaining at end of move
695 "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
702 void spawnfunc_func_button()
706 if not(InitMovingBrushTrigger())
708 self.effects |= EF_LOWPRECISION;
710 self.blocked = button_blocked;
711 self.use = button_use;
713 // if (self.health == 0) // all buttons are now shootable
717 self.max_health = self.health;
718 self.event_damage = button_damage;
719 self.takedamage = DAMAGE_YES;
722 self.touch = button_touch;
732 precache_sound(self.noise);
734 self.active = ACTIVE_ACTIVE;
736 self.pos1 = self.origin;
737 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
738 self.flags |= FL_NOTARGET;
744 float DOOR_START_OPEN = 1;
745 float DOOR_DONT_LINK = 4;
746 float DOOR_TOGGLE = 32;
750 Doors are similar to buttons, but can spawn a fat trigger field around them
751 to open without a touch, and they link together to form simultanious
754 Door.owner is the master door. If there is only one door, it points to itself.
755 If multiple doors, all will point to a single one.
757 Door.enemy chains from the master door through all doors linked in the chain.
762 =============================================================================
766 =============================================================================
771 void() door_rotating_go_down;
772 void() door_rotating_go_up;
777 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
778 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
781 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
782 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
784 //Dont chamge direction for dead or dying stuff
785 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
788 if (self.state == STATE_DOWN)
789 if (self.classname == "door")
794 door_rotating_go_up ();
797 if (self.classname == "door")
802 door_rotating_go_down ();
806 //gib dying stuff just to make sure
807 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
808 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
812 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
813 // if a door has a negative wait, it would never come back if blocked,
814 // so let it just squash the object to death real fast
815 /* if (self.wait >= 0)
817 if (self.state == STATE_DOWN)
828 if (self.noise1 != "")
829 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
830 self.state = STATE_TOP;
831 if (self.spawnflags & DOOR_TOGGLE)
832 return; // don't come down automatically
833 if (self.classname == "door")
835 self.think = door_go_down;
838 self.think = door_rotating_go_down;
840 self.nextthink = self.ltime + self.wait;
843 void door_hit_bottom()
845 if (self.noise1 != "")
846 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
847 self.state = STATE_BOTTOM;
852 if (self.noise2 != "")
853 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
856 self.takedamage = DAMAGE_YES;
857 self.health = self.max_health;
860 self.state = STATE_DOWN;
861 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
866 if (self.state == STATE_UP)
867 return; // already going up
869 if (self.state == STATE_TOP)
870 { // reset top wait time
871 self.nextthink = self.ltime + self.wait;
875 if (self.noise2 != "")
876 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
877 self.state = STATE_UP;
878 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
881 oldmessage = self.message;
884 self.message = oldmessage;
890 =============================================================================
894 =============================================================================
897 float door_check_keys(void) {
898 local entity door, p;
911 if (door.spawnflags & (SPAWNFLAGS_GOLD_KEY | SPAWNFLAGS_SILVER_KEY)) {
912 // this door require a key
913 // only a player can have a key
914 if (p.classname != "player")
918 if (self.owner.spawnflags & SPAWNFLAGS_GOLD_KEY) {
919 if (!(other.itemkeys & KEYS_GOLD_KEY)) {
920 if (p.key_door_messagetime <= time) {
921 play2(other, "misc/talk.wav");
922 centerprint(other, "You don't have the gold key!");
923 p.key_door_messagetime = time + 2;
927 self.owner.spawnflags &~= SPAWNFLAGS_GOLD_KEY;
932 if (self.owner.spawnflags & SPAWNFLAGS_SILVER_KEY) {
933 if (!(other.itemkeys & KEYS_SILVER_KEY)) {
934 if (p.key_door_messagetime <= time) {
935 play2(other, "misc/talk.wav");
936 centerprint(other, "You don't have the silver key!");
937 p.wait = p.key_door_messagetime + 2;
941 self.owner.spawnflags &~= SPAWNFLAGS_SILVER_KEY;
945 // door is now unlocked
946 play2(other, "misc/talk.wav");
947 centerprint(other, "Door unlocked!");
959 if (self.owner != self)
960 objerror ("door_fire: self.owner != self");
964 if (self.spawnflags & DOOR_TOGGLE)
966 if (self.state == STATE_UP || self.state == STATE_TOP)
971 if (self.classname == "door")
977 door_rotating_go_down ();
980 } while ( (self != starte) && (self != world) );
986 // trigger all paired doors
990 if (self.classname == "door")
995 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
996 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
998 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
999 self.pos2 = '0 0 0' - self.pos2;
1001 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1002 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1003 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1005 door_rotating_go_up ();
1009 } while ( (self != starte) && (self != world) );
1018 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1030 void door_trigger_touch()
1032 if (other.health < 1)
1033 if not(other.iscreature && other.deadflag == DEAD_NO)
1036 if (time < self.attack_finished_single)
1039 // check if door is locked
1040 if (!door_check_keys())
1043 self.attack_finished_single = time + 1;
1052 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1055 if(self.spawnflags & DOOR_NOSPLASH)
1056 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1058 self.health = self.health - damage;
1060 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY || self.spawnflags & SPAWNFLAGS_SILVER_KEY) {
1061 // don't allow opening doors through damage if keys are required
1065 if (self.health <= 0)
1069 self.health = self.max_health;
1070 self.takedamage = DAMAGE_NO; // wil be reset upon return
1086 if(other.classname != "player")
1088 if (self.owner.attack_finished_single > time)
1091 self.owner.attack_finished_single = time + 2;
1093 if (!(self.owner.dmg) && (self.owner.message != ""))
1095 if (other.flags & FL_CLIENT)
1096 centerprint (other, self.owner.message);
1097 play2(other, "misc/talk.wav");
1102 void door_generic_plat_blocked()
1105 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1106 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1109 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1110 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1112 //Dont chamge direction for dead or dying stuff
1113 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1116 if (self.state == STATE_DOWN)
1117 door_rotating_go_up ();
1119 door_rotating_go_down ();
1122 //gib dying stuff just to make sure
1123 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1124 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1128 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1129 // if a door has a negative wait, it would never come back if blocked,
1130 // so let it just squash the object to death real fast
1131 /* if (self.wait >= 0)
1133 if (self.state == STATE_DOWN)
1134 door_rotating_go_up ();
1136 door_rotating_go_down ();
1142 void door_rotating_hit_top()
1144 if (self.noise1 != "")
1145 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1146 self.state = STATE_TOP;
1147 if (self.spawnflags & DOOR_TOGGLE)
1148 return; // don't come down automatically
1149 self.think = door_rotating_go_down;
1150 self.nextthink = self.ltime + self.wait;
1153 void door_rotating_hit_bottom()
1155 if (self.noise1 != "")
1156 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1157 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1159 self.pos2 = '0 0 0' - self.pos2;
1162 self.state = STATE_BOTTOM;
1165 void door_rotating_go_down()
1167 if (self.noise2 != "")
1168 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1169 if (self.max_health)
1171 self.takedamage = DAMAGE_YES;
1172 self.health = self.max_health;
1175 self.state = STATE_DOWN;
1176 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1179 void door_rotating_go_up()
1181 if (self.state == STATE_UP)
1182 return; // already going up
1184 if (self.state == STATE_TOP)
1185 { // reset top wait time
1186 self.nextthink = self.ltime + self.wait;
1189 if (self.noise2 != "")
1190 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1191 self.state = STATE_UP;
1192 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1195 oldmessage = self.message;
1198 self.message = oldmessage;
1205 =============================================================================
1209 =============================================================================
1213 entity spawn_field(vector fmins, vector fmaxs)
1215 local entity trigger;
1216 local vector t1, t2;
1219 trigger.classname = "doortriggerfield";
1220 trigger.movetype = MOVETYPE_NONE;
1221 trigger.solid = SOLID_TRIGGER;
1222 trigger.owner = self;
1223 trigger.touch = door_trigger_touch;
1227 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1232 float EntitiesTouching(entity e1, entity e2)
1234 if (e1.absmin_x > e2.absmax_x)
1236 if (e1.absmin_y > e2.absmax_y)
1238 if (e1.absmin_z > e2.absmax_z)
1240 if (e1.absmax_x < e2.absmin_x)
1242 if (e1.absmax_y < e2.absmin_y)
1244 if (e1.absmax_z < e2.absmin_z)
1259 local entity t, starte;
1260 local vector cmins, cmaxs;
1263 return; // already linked by another door
1264 if (self.spawnflags & 4)
1266 self.owner = self.enemy = self;
1274 self.trigger_field = spawn_field(self.absmin, self.absmax);
1276 return; // don't want to link this door
1279 cmins = self.absmin;
1280 cmaxs = self.absmax;
1287 self.owner = starte; // master door
1290 starte.health = self.health;
1292 starte.targetname = self.targetname;
1293 if (self.message != "")
1294 starte.message = self.message;
1296 t = find(t, classname, self.classname);
1299 self.enemy = starte; // make the chain a loop
1301 // shootable, or triggered doors just needed the owner/enemy links,
1302 // they don't spawn a field
1313 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1318 if (EntitiesTouching(self,t))
1321 objerror ("cross connected doors");
1326 if (t.absmin_x < cmins_x)
1327 cmins_x = t.absmin_x;
1328 if (t.absmin_y < cmins_y)
1329 cmins_y = t.absmin_y;
1330 if (t.absmin_z < cmins_z)
1331 cmins_z = t.absmin_z;
1332 if (t.absmax_x > cmaxs_x)
1333 cmaxs_x = t.absmax_x;
1334 if (t.absmax_y > cmaxs_y)
1335 cmaxs_y = t.absmax_y;
1336 if (t.absmax_z > cmaxs_z)
1337 cmaxs_z = t.absmax_z;
1344 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1345 if two doors touch, they are assumed to be connected and operate as a unit.
1347 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1349 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).
1351 GOLD_KEY causes the door to open only if the activator holds a gold key.
1353 SILVER_KEY causes the door to open only if the activator holds a silver key.
1355 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1356 "angle" determines the opening direction
1357 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1358 "health" if set, door must be shot open
1359 "speed" movement speed (100 default)
1360 "wait" wait before returning (3 default, -1 = never return)
1361 "lip" lip remaining at end of move (8 default)
1362 "dmg" damage to inflict when blocked (2 default)
1369 FIXME: only one sound set available at the time being
1373 void door_init_startopen()
1375 setorigin (self, self.pos2);
1376 self.pos2 = self.pos1;
1377 self.pos1 = self.origin;
1382 setorigin(self, self.pos1);
1383 self.velocity = '0 0 0';
1384 self.state = STATE_BOTTOM;
1385 self.think = SUB_Null;
1388 void spawnfunc_func_door()
1390 print("spawnfunc_func_door() spawnflags=", ftos(self.spawnflags));
1391 print(", gold_key=", ftos(self.spawnflags & SPAWNFLAGS_GOLD_KEY));
1392 print(", silver_key=", ftos(self.spawnflags & SPAWNFLAGS_SILVER_KEY), "\n");
1394 //if (!self.deathtype) // map makers can override this
1395 // self.deathtype = " got in the way";
1398 self.max_health = self.health;
1399 if not(InitMovingBrushTrigger())
1401 self.effects |= EF_LOWPRECISION;
1402 self.classname = "door";
1404 self.blocked = door_blocked;
1405 self.use = door_use;
1407 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1408 // if(self.spawnflags & 8)
1409 // self.dmg = 10000;
1411 if(self.dmg && (!self.message))
1412 self.message = "was squished";
1413 if(self.dmg && (!self.message2))
1414 self.message2 = "was squished by";
1416 if (self.sounds > 0)
1418 precache_sound ("plats/medplat1.wav");
1419 precache_sound ("plats/medplat2.wav");
1420 self.noise2 = "plats/medplat1.wav";
1421 self.noise1 = "plats/medplat2.wav";
1431 self.pos1 = self.origin;
1432 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1434 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1435 // but spawn in the open position
1436 if (self.spawnflags & DOOR_START_OPEN)
1437 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1439 self.state = STATE_BOTTOM;
1443 self.takedamage = DAMAGE_YES;
1444 self.event_damage = door_damage;
1450 self.touch = door_touch;
1452 // LinkDoors can't be done until all of the doors have been spawned, so
1453 // the sizes can be detected properly.
1454 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1456 self.reset = door_reset;
1459 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1460 if two doors touch, they are assumed to be connected and operate as a unit.
1462 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1464 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1465 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1466 must have set trigger_reverse to 1.
1467 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1469 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).
1471 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1472 "angle" determines the destination angle for opening. negative values reverse the direction.
1473 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1474 "health" if set, door must be shot open
1475 "speed" movement speed (100 default)
1476 "wait" wait before returning (3 default, -1 = never return)
1477 "dmg" damage to inflict when blocked (2 default)
1484 FIXME: only one sound set available at the time being
1487 void door_rotating_reset()
1489 self.angles = self.pos1;
1490 self.avelocity = '0 0 0';
1491 self.state = STATE_BOTTOM;
1492 self.think = SUB_Null;
1495 void door_rotating_init_startopen()
1497 self.angles = self.movedir;
1498 self.pos2 = '0 0 0';
1499 self.pos1 = self.movedir;
1503 void spawnfunc_func_door_rotating()
1506 //if (!self.deathtype) // map makers can override this
1507 // self.deathtype = " got in the way";
1509 // I abuse "movedir" for denoting the axis for now
1510 if (self.spawnflags & 64) // X (untested)
1511 self.movedir = '0 0 1';
1512 else if (self.spawnflags & 128) // Y (untested)
1513 self.movedir = '1 0 0';
1515 self.movedir = '0 1 0';
1517 if (self.angles_y==0) self.angles_y = 90;
1519 self.movedir = self.movedir * self.angles_y;
1520 self.angles = '0 0 0';
1522 self.max_health = self.health;
1523 self.avelocity = self.movedir;
1524 if not(InitMovingBrushTrigger())
1526 self.velocity = '0 0 0';
1527 //self.effects |= EF_LOWPRECISION;
1528 self.classname = "door_rotating";
1530 self.blocked = door_blocked;
1531 self.use = door_use;
1533 if(self.spawnflags & 8)
1536 if(self.dmg && (!self.message))
1537 self.message = "was squished";
1538 if(self.dmg && (!self.message2))
1539 self.message2 = "was squished by";
1541 if (self.sounds > 0)
1543 precache_sound ("plats/medplat1.wav");
1544 precache_sound ("plats/medplat2.wav");
1545 self.noise2 = "plats/medplat1.wav";
1546 self.noise1 = "plats/medplat2.wav";
1553 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1555 self.pos1 = '0 0 0';
1556 self.pos2 = self.movedir;
1558 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1559 // but spawn in the open position
1560 if (self.spawnflags & DOOR_START_OPEN)
1561 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1563 self.state = STATE_BOTTOM;
1567 self.takedamage = DAMAGE_YES;
1568 self.event_damage = door_damage;
1574 self.touch = door_touch;
1576 // LinkDoors can't be done until all of the doors have been spawned, so
1577 // the sizes can be detected properly.
1578 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1580 self.reset = door_rotating_reset;
1584 =============================================================================
1588 =============================================================================
1591 void() fd_secret_move1;
1592 void() fd_secret_move2;
1593 void() fd_secret_move3;
1594 void() fd_secret_move4;
1595 void() fd_secret_move5;
1596 void() fd_secret_move6;
1597 void() fd_secret_done;
1599 float SECRET_OPEN_ONCE = 1; // stays open
1600 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1601 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1602 float SECRET_NO_SHOOT = 8; // only opened by trigger
1603 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1606 void fd_secret_use()
1609 string message_save;
1611 self.health = 10000;
1612 self.bot_attack = TRUE;
1614 // exit if still moving around...
1615 if (self.origin != self.oldorigin)
1618 message_save = self.message;
1619 self.message = ""; // no more message
1620 SUB_UseTargets(); // fire all targets / killtargets
1621 self.message = message_save;
1623 self.velocity = '0 0 0';
1625 // Make a sound, wait a little...
1627 if (self.noise1 != "")
1628 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1629 self.nextthink = self.ltime + 0.1;
1631 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1632 makevectors(self.mangle);
1636 if (self.spawnflags & SECRET_1ST_DOWN)
1637 self.t_width = fabs(v_up * self.size);
1639 self.t_width = fabs(v_right * self.size);
1643 self.t_length = fabs(v_forward * self.size);
1645 if (self.spawnflags & SECRET_1ST_DOWN)
1646 self.dest1 = self.origin - v_up * self.t_width;
1648 self.dest1 = self.origin + v_right * (self.t_width * temp);
1650 self.dest2 = self.dest1 + v_forward * self.t_length;
1651 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1652 if (self.noise2 != "")
1653 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1656 // Wait after first movement...
1657 void fd_secret_move1()
1659 self.nextthink = self.ltime + 1.0;
1660 self.think = fd_secret_move2;
1661 if (self.noise3 != "")
1662 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1665 // Start moving sideways w/sound...
1666 void fd_secret_move2()
1668 if (self.noise2 != "")
1669 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1670 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1673 // Wait here until time to go back...
1674 void fd_secret_move3()
1676 if (self.noise3 != "")
1677 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1678 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1680 self.nextthink = self.ltime + self.wait;
1681 self.think = fd_secret_move4;
1686 void fd_secret_move4()
1688 if (self.noise2 != "")
1689 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1690 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1694 void fd_secret_move5()
1696 self.nextthink = self.ltime + 1.0;
1697 self.think = fd_secret_move6;
1698 if (self.noise3 != "")
1699 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1702 void fd_secret_move6()
1704 if (self.noise2 != "")
1705 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1706 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1709 void fd_secret_done()
1711 if (self.spawnflags&SECRET_YES_SHOOT)
1713 self.health = 10000;
1714 self.takedamage = DAMAGE_YES;
1715 //self.th_pain = fd_secret_use;
1717 if (self.noise3 != "")
1718 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1721 void secret_blocked()
1723 if (time < self.attack_finished_single)
1725 self.attack_finished_single = time + 0.5;
1726 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1738 if not(other.iscreature)
1740 if (self.attack_finished_single > time)
1743 self.attack_finished_single = time + 2;
1747 if (other.flags & FL_CLIENT)
1748 centerprint (other, self.message);
1749 play2(other, "misc/talk.wav");
1755 if (self.spawnflags&SECRET_YES_SHOOT)
1757 self.health = 10000;
1758 self.takedamage = DAMAGE_YES;
1760 setorigin(self, self.oldorigin);
1761 self.think = SUB_Null;
1764 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1765 Basic secret door. Slides back, then to the side. Angle determines direction.
1766 wait = # of seconds before coming back
1767 1st_left = 1st move is left of arrow
1768 1st_down = 1st move is down from arrow
1769 always_shoot = even if targeted, keep shootable
1770 t_width = override WIDTH to move back (or height if going down)
1771 t_length = override LENGTH to move sideways
1772 "dmg" damage to inflict when blocked (2 default)
1774 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1781 void spawnfunc_func_door_secret()
1783 /*if (!self.deathtype) // map makers can override this
1784 self.deathtype = " got in the way";*/
1790 self.mangle = self.angles;
1791 self.angles = '0 0 0';
1792 self.classname = "door";
1793 if not(InitMovingBrushTrigger())
1795 self.effects |= EF_LOWPRECISION;
1797 self.touch = secret_touch;
1798 self.blocked = secret_blocked;
1800 self.use = fd_secret_use;
1805 self.spawnflags |= SECRET_YES_SHOOT;
1807 if(self.spawnflags&SECRET_YES_SHOOT)
1809 self.health = 10000;
1810 self.takedamage = DAMAGE_YES;
1811 self.event_damage = fd_secret_use;
1813 self.oldorigin = self.origin;
1815 self.wait = 5; // 5 seconds before closing
1817 self.reset = secret_reset;
1821 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1822 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1823 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
1824 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1825 height: amplitude modifier (default 32)
1826 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1827 noise: path/name of looping .wav file to play.
1828 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1832 void func_fourier_controller_think()
1837 self.nextthink = time + 0.1;
1838 if not (self.owner.active == ACTIVE_ACTIVE)
1840 self.owner.velocity = '0 0 0';
1845 n = floor((tokenize_console(self.owner.netname)) / 5);
1846 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1848 v = self.owner.destvec;
1850 for(i = 0; i < n; ++i)
1852 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1853 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;
1856 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1857 // * 10 so it will arrive in 0.1 sec
1858 self.owner.velocity = (v - self.owner.origin) * 10;
1861 void spawnfunc_func_fourier()
1863 local entity controller;
1864 if (self.noise != "")
1866 precache_sound(self.noise);
1867 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1874 self.destvec = self.origin;
1875 self.cnt = 360 / self.speed;
1877 self.blocked = generic_plat_blocked;
1878 if(self.dmg & (!self.message))
1879 self.message = " was squished";
1880 if(self.dmg && (!self.message2))
1881 self.message2 = "was squished by";
1882 if(self.dmg && (!self.dmgtime))
1883 self.dmgtime = 0.25;
1884 self.dmgtime2 = time;
1886 if(self.netname == "")
1887 self.netname = "1 0 0 0 1";
1889 if not(InitMovingBrushTrigger())
1892 self.active = ACTIVE_ACTIVE;
1894 // wait for targets to spawn
1895 controller = spawn();
1896 controller.classname = "func_fourier_controller";
1897 controller.owner = self;
1898 controller.nextthink = time + 1;
1899 controller.think = func_fourier_controller_think;
1900 self.nextthink = self.ltime + 999999999;
1901 self.think = SUB_Null;
1903 // Savage: Reduce bandwith, critical on e.g. nexdm02
1904 self.effects |= EF_LOWPRECISION;
1906 // TODO make a reset function for this one
1909 // reusing some fields havocbots declared
1910 .entity wp00, wp01, wp02, wp03;
1912 .float targetfactor, target2factor, target3factor, target4factor;
1913 .vector targetnormal, target2normal, target3normal, target4normal;
1915 vector func_vectormamamam_origin(entity o, float t)
1927 p = e.origin + t * e.velocity;
1929 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1931 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1937 p = e.origin + t * e.velocity;
1939 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1941 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1947 p = e.origin + t * e.velocity;
1949 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1951 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1957 p = e.origin + t * e.velocity;
1959 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1961 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1967 void func_vectormamamam_controller_think()
1969 self.nextthink = time + 0.1;
1971 if not (self.owner.active == ACTIVE_ACTIVE)
1973 self.owner.velocity = '0 0 0';
1977 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1978 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1981 void func_vectormamamam_findtarget()
1983 if(self.target != "")
1984 self.wp00 = find(world, targetname, self.target);
1986 if(self.target2 != "")
1987 self.wp01 = find(world, targetname, self.target2);
1989 if(self.target3 != "")
1990 self.wp02 = find(world, targetname, self.target3);
1992 if(self.target4 != "")
1993 self.wp03 = find(world, targetname, self.target4);
1995 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1996 objerror("No reference entity found, so there is nothing to move. Aborting.");
1998 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2000 local entity controller;
2001 controller = spawn();
2002 controller.classname = "func_vectormamamam_controller";
2003 controller.owner = self;
2004 controller.nextthink = time + 1;
2005 controller.think = func_vectormamamam_controller_think;
2008 void spawnfunc_func_vectormamamam()
2010 if (self.noise != "")
2012 precache_sound(self.noise);
2013 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2016 if(!self.targetfactor)
2017 self.targetfactor = 1;
2019 if(!self.target2factor)
2020 self.target2factor = 1;
2022 if(!self.target3factor)
2023 self.target3factor = 1;
2025 if(!self.target4factor)
2026 self.target4factor = 1;
2028 if(vlen(self.targetnormal))
2029 self.targetnormal = normalize(self.targetnormal);
2031 if(vlen(self.target2normal))
2032 self.target2normal = normalize(self.target2normal);
2034 if(vlen(self.target3normal))
2035 self.target3normal = normalize(self.target3normal);
2037 if(vlen(self.target4normal))
2038 self.target4normal = normalize(self.target4normal);
2040 self.blocked = generic_plat_blocked;
2041 if(self.dmg & (!self.message))
2042 self.message = " was squished";
2043 if(self.dmg && (!self.message2))
2044 self.message2 = "was squished by";
2045 if(self.dmg && (!self.dmgtime))
2046 self.dmgtime = 0.25;
2047 self.dmgtime2 = time;
2049 if(self.netname == "")
2050 self.netname = "1 0 0 0 1";
2052 if not(InitMovingBrushTrigger())
2055 // wait for targets to spawn
2056 self.nextthink = self.ltime + 999999999;
2057 self.think = SUB_Null;
2059 // Savage: Reduce bandwith, critical on e.g. nexdm02
2060 self.effects |= EF_LOWPRECISION;
2062 self.active = ACTIVE_ACTIVE;
2064 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);