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) {
906 if (door.spawnflags & (SPAWNFLAGS_GOLD_KEY | SPAWNFLAGS_SILVER_KEY)) {
907 // this door require a key
908 // only a player can have a key
909 if (other.classname != "player")
913 if (self.owner.spawnflags & SPAWNFLAGS_GOLD_KEY) {
914 if (!(other.itemkeys & KEYS_GOLD_KEY)) {
915 if (other.key_door_messagetime <= time) {
916 play2(other, "misc/talk.wav");
917 centerprint(other, "You don't have the gold key!");
918 other.key_door_messagetime = time + 2;
922 self.owner.spawnflags &~= SPAWNFLAGS_GOLD_KEY;
927 if (self.owner.spawnflags & SPAWNFLAGS_SILVER_KEY) {
928 if (!(other.itemkeys & KEYS_SILVER_KEY)) {
929 if (other.key_door_messagetime <= time) {
930 play2(other, "misc/talk.wav");
931 centerprint(other, "You don't have the silver key!");
932 other.key_door_messagetime = time + 2;
936 self.owner.spawnflags &~= SPAWNFLAGS_SILVER_KEY;
940 // door is now unlocked
941 play2(other, "misc/talk.wav");
942 centerprint(other, "Door unlocked!");
954 if (self.owner != self)
955 objerror ("door_fire: self.owner != self");
959 if (self.spawnflags & DOOR_TOGGLE)
961 if (self.state == STATE_UP || self.state == STATE_TOP)
966 if (self.classname == "door")
972 door_rotating_go_down ();
975 } while ( (self != starte) && (self != world) );
981 // trigger all paired doors
985 if (self.classname == "door")
990 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
991 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
993 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
994 self.pos2 = '0 0 0' - self.pos2;
996 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
997 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
998 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1000 door_rotating_go_up ();
1004 } while ( (self != starte) && (self != world) );
1013 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1025 void door_trigger_touch()
1027 if (other.health < 1)
1028 if not(other.iscreature && other.deadflag == DEAD_NO)
1031 if (time < self.attack_finished_single)
1034 // check if door is locked
1035 if (!door_check_keys())
1038 self.attack_finished_single = time + 1;
1047 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1050 if(self.spawnflags & DOOR_NOSPLASH)
1051 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1053 self.health = self.health - damage;
1055 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY || self.spawnflags & SPAWNFLAGS_SILVER_KEY) {
1056 // don't allow opening doors through damage if keys are required
1060 if (self.health <= 0)
1064 self.health = self.max_health;
1065 self.takedamage = DAMAGE_NO; // wil be reset upon return
1081 if(other.classname != "player")
1083 if (self.owner.attack_finished_single > time)
1086 self.owner.attack_finished_single = time + 2;
1088 if (!(self.owner.dmg) && (self.owner.message != ""))
1090 if (other.flags & FL_CLIENT)
1091 centerprint (other, self.owner.message);
1092 play2(other, "misc/talk.wav");
1097 void door_generic_plat_blocked()
1100 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1101 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1104 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1105 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1107 //Dont chamge direction for dead or dying stuff
1108 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1111 if (self.state == STATE_DOWN)
1112 door_rotating_go_up ();
1114 door_rotating_go_down ();
1117 //gib dying stuff just to make sure
1118 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1119 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1123 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1124 // if a door has a negative wait, it would never come back if blocked,
1125 // so let it just squash the object to death real fast
1126 /* if (self.wait >= 0)
1128 if (self.state == STATE_DOWN)
1129 door_rotating_go_up ();
1131 door_rotating_go_down ();
1137 void door_rotating_hit_top()
1139 if (self.noise1 != "")
1140 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1141 self.state = STATE_TOP;
1142 if (self.spawnflags & DOOR_TOGGLE)
1143 return; // don't come down automatically
1144 self.think = door_rotating_go_down;
1145 self.nextthink = self.ltime + self.wait;
1148 void door_rotating_hit_bottom()
1150 if (self.noise1 != "")
1151 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1152 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1154 self.pos2 = '0 0 0' - self.pos2;
1157 self.state = STATE_BOTTOM;
1160 void door_rotating_go_down()
1162 if (self.noise2 != "")
1163 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1164 if (self.max_health)
1166 self.takedamage = DAMAGE_YES;
1167 self.health = self.max_health;
1170 self.state = STATE_DOWN;
1171 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1174 void door_rotating_go_up()
1176 if (self.state == STATE_UP)
1177 return; // already going up
1179 if (self.state == STATE_TOP)
1180 { // reset top wait time
1181 self.nextthink = self.ltime + self.wait;
1184 if (self.noise2 != "")
1185 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1186 self.state = STATE_UP;
1187 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1190 oldmessage = self.message;
1193 self.message = oldmessage;
1200 =============================================================================
1204 =============================================================================
1208 entity spawn_field(vector fmins, vector fmaxs)
1210 local entity trigger;
1211 local vector t1, t2;
1214 trigger.classname = "doortriggerfield";
1215 trigger.movetype = MOVETYPE_NONE;
1216 trigger.solid = SOLID_TRIGGER;
1217 trigger.owner = self;
1218 trigger.touch = door_trigger_touch;
1222 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1227 float EntitiesTouching(entity e1, entity e2)
1229 if (e1.absmin_x > e2.absmax_x)
1231 if (e1.absmin_y > e2.absmax_y)
1233 if (e1.absmin_z > e2.absmax_z)
1235 if (e1.absmax_x < e2.absmin_x)
1237 if (e1.absmax_y < e2.absmin_y)
1239 if (e1.absmax_z < e2.absmin_z)
1254 local entity t, starte;
1255 local vector cmins, cmaxs;
1258 return; // already linked by another door
1259 if (self.spawnflags & 4)
1261 self.owner = self.enemy = self;
1269 self.trigger_field = spawn_field(self.absmin, self.absmax);
1271 return; // don't want to link this door
1274 cmins = self.absmin;
1275 cmaxs = self.absmax;
1282 self.owner = starte; // master door
1285 starte.health = self.health;
1287 starte.targetname = self.targetname;
1288 if (self.message != "")
1289 starte.message = self.message;
1291 t = find(t, classname, self.classname);
1294 self.enemy = starte; // make the chain a loop
1296 // shootable, or triggered doors just needed the owner/enemy links,
1297 // they don't spawn a field
1308 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1313 if (EntitiesTouching(self,t))
1316 objerror ("cross connected doors");
1321 if (t.absmin_x < cmins_x)
1322 cmins_x = t.absmin_x;
1323 if (t.absmin_y < cmins_y)
1324 cmins_y = t.absmin_y;
1325 if (t.absmin_z < cmins_z)
1326 cmins_z = t.absmin_z;
1327 if (t.absmax_x > cmaxs_x)
1328 cmaxs_x = t.absmax_x;
1329 if (t.absmax_y > cmaxs_y)
1330 cmaxs_y = t.absmax_y;
1331 if (t.absmax_z > cmaxs_z)
1332 cmaxs_z = t.absmax_z;
1339 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1340 if two doors touch, they are assumed to be connected and operate as a unit.
1342 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1344 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).
1346 GOLD_KEY causes the door to open only if the activator holds a gold key.
1348 SILVER_KEY causes the door to open only if the activator holds a silver key.
1350 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1351 "angle" determines the opening direction
1352 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1353 "health" if set, door must be shot open
1354 "speed" movement speed (100 default)
1355 "wait" wait before returning (3 default, -1 = never return)
1356 "lip" lip remaining at end of move (8 default)
1357 "dmg" damage to inflict when blocked (2 default)
1364 FIXME: only one sound set available at the time being
1368 void door_init_startopen()
1370 setorigin (self, self.pos2);
1371 self.pos2 = self.pos1;
1372 self.pos1 = self.origin;
1377 setorigin(self, self.pos1);
1378 self.velocity = '0 0 0';
1379 self.state = STATE_BOTTOM;
1380 self.think = SUB_Null;
1383 void spawnfunc_func_door()
1385 //dprint("spawnfunc_func_door() spawnflags=", ftos(self.spawnflags));
1386 //dprint(", gold_key=", ftos(self.spawnflags & SPAWNFLAGS_GOLD_KEY));
1387 //dprint(", silver_key=", ftos(self.spawnflags & SPAWNFLAGS_SILVER_KEY), "\n");
1389 //if (!self.deathtype) // map makers can override this
1390 // self.deathtype = " got in the way";
1393 self.max_health = self.health;
1394 if not(InitMovingBrushTrigger())
1396 self.effects |= EF_LOWPRECISION;
1397 self.classname = "door";
1399 self.blocked = door_blocked;
1400 self.use = door_use;
1402 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1403 // if(self.spawnflags & 8)
1404 // self.dmg = 10000;
1406 if(self.dmg && (!self.message))
1407 self.message = "was squished";
1408 if(self.dmg && (!self.message2))
1409 self.message2 = "was squished by";
1411 if (self.sounds > 0)
1413 precache_sound ("plats/medplat1.wav");
1414 precache_sound ("plats/medplat2.wav");
1415 self.noise2 = "plats/medplat1.wav";
1416 self.noise1 = "plats/medplat2.wav";
1426 self.pos1 = self.origin;
1427 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1429 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1430 // but spawn in the open position
1431 if (self.spawnflags & DOOR_START_OPEN)
1432 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1434 self.state = STATE_BOTTOM;
1438 self.takedamage = DAMAGE_YES;
1439 self.event_damage = door_damage;
1445 self.touch = door_touch;
1447 // LinkDoors can't be done until all of the doors have been spawned, so
1448 // the sizes can be detected properly.
1449 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1451 self.reset = door_reset;
1454 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1455 if two doors touch, they are assumed to be connected and operate as a unit.
1457 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1459 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1460 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1461 must have set trigger_reverse to 1.
1462 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1464 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).
1466 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1467 "angle" determines the destination angle for opening. negative values reverse the direction.
1468 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1469 "health" if set, door must be shot open
1470 "speed" movement speed (100 default)
1471 "wait" wait before returning (3 default, -1 = never return)
1472 "dmg" damage to inflict when blocked (2 default)
1479 FIXME: only one sound set available at the time being
1482 void door_rotating_reset()
1484 self.angles = self.pos1;
1485 self.avelocity = '0 0 0';
1486 self.state = STATE_BOTTOM;
1487 self.think = SUB_Null;
1490 void door_rotating_init_startopen()
1492 self.angles = self.movedir;
1493 self.pos2 = '0 0 0';
1494 self.pos1 = self.movedir;
1498 void spawnfunc_func_door_rotating()
1501 //if (!self.deathtype) // map makers can override this
1502 // self.deathtype = " got in the way";
1504 // I abuse "movedir" for denoting the axis for now
1505 if (self.spawnflags & 64) // X (untested)
1506 self.movedir = '0 0 1';
1507 else if (self.spawnflags & 128) // Y (untested)
1508 self.movedir = '1 0 0';
1510 self.movedir = '0 1 0';
1512 if (self.angles_y==0) self.angles_y = 90;
1514 self.movedir = self.movedir * self.angles_y;
1515 self.angles = '0 0 0';
1517 self.max_health = self.health;
1518 self.avelocity = self.movedir;
1519 if not(InitMovingBrushTrigger())
1521 self.velocity = '0 0 0';
1522 //self.effects |= EF_LOWPRECISION;
1523 self.classname = "door_rotating";
1525 self.blocked = door_blocked;
1526 self.use = door_use;
1528 if(self.spawnflags & 8)
1531 if(self.dmg && (!self.message))
1532 self.message = "was squished";
1533 if(self.dmg && (!self.message2))
1534 self.message2 = "was squished by";
1536 if (self.sounds > 0)
1538 precache_sound ("plats/medplat1.wav");
1539 precache_sound ("plats/medplat2.wav");
1540 self.noise2 = "plats/medplat1.wav";
1541 self.noise1 = "plats/medplat2.wav";
1548 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1550 self.pos1 = '0 0 0';
1551 self.pos2 = self.movedir;
1553 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1554 // but spawn in the open position
1555 if (self.spawnflags & DOOR_START_OPEN)
1556 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1558 self.state = STATE_BOTTOM;
1562 self.takedamage = DAMAGE_YES;
1563 self.event_damage = door_damage;
1569 self.touch = door_touch;
1571 // LinkDoors can't be done until all of the doors have been spawned, so
1572 // the sizes can be detected properly.
1573 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1575 self.reset = door_rotating_reset;
1579 =============================================================================
1583 =============================================================================
1586 void() fd_secret_move1;
1587 void() fd_secret_move2;
1588 void() fd_secret_move3;
1589 void() fd_secret_move4;
1590 void() fd_secret_move5;
1591 void() fd_secret_move6;
1592 void() fd_secret_done;
1594 float SECRET_OPEN_ONCE = 1; // stays open
1595 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1596 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1597 float SECRET_NO_SHOOT = 8; // only opened by trigger
1598 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1601 void fd_secret_use()
1604 string message_save;
1606 self.health = 10000;
1607 self.bot_attack = TRUE;
1609 // exit if still moving around...
1610 if (self.origin != self.oldorigin)
1613 message_save = self.message;
1614 self.message = ""; // no more message
1615 SUB_UseTargets(); // fire all targets / killtargets
1616 self.message = message_save;
1618 self.velocity = '0 0 0';
1620 // Make a sound, wait a little...
1622 if (self.noise1 != "")
1623 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1624 self.nextthink = self.ltime + 0.1;
1626 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1627 makevectors(self.mangle);
1631 if (self.spawnflags & SECRET_1ST_DOWN)
1632 self.t_width = fabs(v_up * self.size);
1634 self.t_width = fabs(v_right * self.size);
1638 self.t_length = fabs(v_forward * self.size);
1640 if (self.spawnflags & SECRET_1ST_DOWN)
1641 self.dest1 = self.origin - v_up * self.t_width;
1643 self.dest1 = self.origin + v_right * (self.t_width * temp);
1645 self.dest2 = self.dest1 + v_forward * self.t_length;
1646 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1647 if (self.noise2 != "")
1648 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1651 // Wait after first movement...
1652 void fd_secret_move1()
1654 self.nextthink = self.ltime + 1.0;
1655 self.think = fd_secret_move2;
1656 if (self.noise3 != "")
1657 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1660 // Start moving sideways w/sound...
1661 void fd_secret_move2()
1663 if (self.noise2 != "")
1664 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1665 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1668 // Wait here until time to go back...
1669 void fd_secret_move3()
1671 if (self.noise3 != "")
1672 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1673 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1675 self.nextthink = self.ltime + self.wait;
1676 self.think = fd_secret_move4;
1681 void fd_secret_move4()
1683 if (self.noise2 != "")
1684 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1685 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1689 void fd_secret_move5()
1691 self.nextthink = self.ltime + 1.0;
1692 self.think = fd_secret_move6;
1693 if (self.noise3 != "")
1694 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1697 void fd_secret_move6()
1699 if (self.noise2 != "")
1700 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1701 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1704 void fd_secret_done()
1706 if (self.spawnflags&SECRET_YES_SHOOT)
1708 self.health = 10000;
1709 self.takedamage = DAMAGE_YES;
1710 //self.th_pain = fd_secret_use;
1712 if (self.noise3 != "")
1713 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1716 void secret_blocked()
1718 if (time < self.attack_finished_single)
1720 self.attack_finished_single = time + 0.5;
1721 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1733 if not(other.iscreature)
1735 if (self.attack_finished_single > time)
1738 self.attack_finished_single = time + 2;
1742 if (other.flags & FL_CLIENT)
1743 centerprint (other, self.message);
1744 play2(other, "misc/talk.wav");
1750 if (self.spawnflags&SECRET_YES_SHOOT)
1752 self.health = 10000;
1753 self.takedamage = DAMAGE_YES;
1755 setorigin(self, self.oldorigin);
1756 self.think = SUB_Null;
1759 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1760 Basic secret door. Slides back, then to the side. Angle determines direction.
1761 wait = # of seconds before coming back
1762 1st_left = 1st move is left of arrow
1763 1st_down = 1st move is down from arrow
1764 always_shoot = even if targeted, keep shootable
1765 t_width = override WIDTH to move back (or height if going down)
1766 t_length = override LENGTH to move sideways
1767 "dmg" damage to inflict when blocked (2 default)
1769 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1776 void spawnfunc_func_door_secret()
1778 /*if (!self.deathtype) // map makers can override this
1779 self.deathtype = " got in the way";*/
1785 self.mangle = self.angles;
1786 self.angles = '0 0 0';
1787 self.classname = "door";
1788 if not(InitMovingBrushTrigger())
1790 self.effects |= EF_LOWPRECISION;
1792 self.touch = secret_touch;
1793 self.blocked = secret_blocked;
1795 self.use = fd_secret_use;
1800 self.spawnflags |= SECRET_YES_SHOOT;
1802 if(self.spawnflags&SECRET_YES_SHOOT)
1804 self.health = 10000;
1805 self.takedamage = DAMAGE_YES;
1806 self.event_damage = fd_secret_use;
1808 self.oldorigin = self.origin;
1810 self.wait = 5; // 5 seconds before closing
1812 self.reset = secret_reset;
1816 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1817 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1818 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
1819 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1820 height: amplitude modifier (default 32)
1821 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1822 noise: path/name of looping .wav file to play.
1823 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1827 void func_fourier_controller_think()
1832 self.nextthink = time + 0.1;
1833 if not (self.owner.active == ACTIVE_ACTIVE)
1835 self.owner.velocity = '0 0 0';
1840 n = floor((tokenize_console(self.owner.netname)) / 5);
1841 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1843 v = self.owner.destvec;
1845 for(i = 0; i < n; ++i)
1847 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1848 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;
1851 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1852 // * 10 so it will arrive in 0.1 sec
1853 self.owner.velocity = (v - self.owner.origin) * 10;
1856 void spawnfunc_func_fourier()
1858 local entity controller;
1859 if (self.noise != "")
1861 precache_sound(self.noise);
1862 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1869 self.destvec = self.origin;
1870 self.cnt = 360 / self.speed;
1872 self.blocked = generic_plat_blocked;
1873 if(self.dmg & (!self.message))
1874 self.message = " was squished";
1875 if(self.dmg && (!self.message2))
1876 self.message2 = "was squished by";
1877 if(self.dmg && (!self.dmgtime))
1878 self.dmgtime = 0.25;
1879 self.dmgtime2 = time;
1881 if(self.netname == "")
1882 self.netname = "1 0 0 0 1";
1884 if not(InitMovingBrushTrigger())
1887 self.active = ACTIVE_ACTIVE;
1889 // wait for targets to spawn
1890 controller = spawn();
1891 controller.classname = "func_fourier_controller";
1892 controller.owner = self;
1893 controller.nextthink = time + 1;
1894 controller.think = func_fourier_controller_think;
1895 self.nextthink = self.ltime + 999999999;
1896 self.think = SUB_Null;
1898 // Savage: Reduce bandwith, critical on e.g. nexdm02
1899 self.effects |= EF_LOWPRECISION;
1901 // TODO make a reset function for this one
1904 // reusing some fields havocbots declared
1905 .entity wp00, wp01, wp02, wp03;
1907 .float targetfactor, target2factor, target3factor, target4factor;
1908 .vector targetnormal, target2normal, target3normal, target4normal;
1910 vector func_vectormamamam_origin(entity o, float t)
1922 p = e.origin + t * e.velocity;
1924 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1926 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1932 p = e.origin + t * e.velocity;
1934 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1936 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1942 p = e.origin + t * e.velocity;
1944 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1946 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1952 p = e.origin + t * e.velocity;
1954 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1956 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1962 void func_vectormamamam_controller_think()
1964 self.nextthink = time + 0.1;
1966 if not (self.owner.active == ACTIVE_ACTIVE)
1968 self.owner.velocity = '0 0 0';
1972 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1973 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1976 void func_vectormamamam_findtarget()
1978 if(self.target != "")
1979 self.wp00 = find(world, targetname, self.target);
1981 if(self.target2 != "")
1982 self.wp01 = find(world, targetname, self.target2);
1984 if(self.target3 != "")
1985 self.wp02 = find(world, targetname, self.target3);
1987 if(self.target4 != "")
1988 self.wp03 = find(world, targetname, self.target4);
1990 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1991 objerror("No reference entity found, so there is nothing to move. Aborting.");
1993 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
1995 local entity controller;
1996 controller = spawn();
1997 controller.classname = "func_vectormamamam_controller";
1998 controller.owner = self;
1999 controller.nextthink = time + 1;
2000 controller.think = func_vectormamamam_controller_think;
2003 void spawnfunc_func_vectormamamam()
2005 if (self.noise != "")
2007 precache_sound(self.noise);
2008 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2011 if(!self.targetfactor)
2012 self.targetfactor = 1;
2014 if(!self.target2factor)
2015 self.target2factor = 1;
2017 if(!self.target3factor)
2018 self.target3factor = 1;
2020 if(!self.target4factor)
2021 self.target4factor = 1;
2023 if(vlen(self.targetnormal))
2024 self.targetnormal = normalize(self.targetnormal);
2026 if(vlen(self.target2normal))
2027 self.target2normal = normalize(self.target2normal);
2029 if(vlen(self.target3normal))
2030 self.target3normal = normalize(self.target3normal);
2032 if(vlen(self.target4normal))
2033 self.target4normal = normalize(self.target4normal);
2035 self.blocked = generic_plat_blocked;
2036 if(self.dmg & (!self.message))
2037 self.message = " was squished";
2038 if(self.dmg && (!self.message2))
2039 self.message2 = "was squished by";
2040 if(self.dmg && (!self.dmgtime))
2041 self.dmgtime = 0.25;
2042 self.dmgtime2 = time;
2044 if(self.netname == "")
2045 self.netname = "1 0 0 0 1";
2047 if not(InitMovingBrushTrigger())
2050 // wait for targets to spawn
2051 self.nextthink = self.ltime + 999999999;
2052 self.think = SUB_Null;
2054 // Savage: Reduce bandwith, critical on e.g. nexdm02
2055 self.effects |= EF_LOWPRECISION;
2057 self.active = ACTIVE_ACTIVE;
2059 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);