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, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, 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 void spawnfunc_path_corner() { }
186 void spawnfunc_func_plat()
188 if (self.sounds == 0)
191 if(self.spawnflags & 4)
194 if(self.dmg && (!self.message))
195 self.message = "was squished";
196 if(self.dmg && (!self.message2))
197 self.message2 = "was squished by";
199 if (self.sounds == 1)
201 precache_sound ("plats/plat1.wav");
202 precache_sound ("plats/plat2.wav");
203 self.noise = "plats/plat1.wav";
204 self.noise1 = "plats/plat2.wav";
207 if (self.sounds == 2)
209 precache_sound ("plats/medplat1.wav");
210 precache_sound ("plats/medplat2.wav");
211 self.noise = "plats/medplat1.wav";
212 self.noise1 = "plats/medplat2.wav";
217 precache_sound (self.sound1);
218 self.noise = self.sound1;
222 precache_sound (self.sound2);
223 self.noise1 = self.sound2;
226 self.mangle = self.angles;
227 self.angles = '0 0 0';
229 self.classname = "plat";
230 if not(InitMovingBrushTrigger())
232 self.effects |= EF_LOWPRECISION;
233 setsize (self, self.mins , self.maxs);
235 self.blocked = plat_crush;
242 self.height = self.size_z - self.lip;
244 self.pos1 = self.origin;
245 self.pos2 = self.origin;
246 self.pos2_z = self.origin_z - self.height;
248 self.reset = plat_reset;
251 plat_spawn_inside_trigger (); // the "start moving" trigger
259 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
267 self.think = train_next;
268 self.nextthink = self.ltime + self.wait;
282 targ = find(world, targetname, self.target);
283 self.target = targ.target;
284 if (self.spawnflags & 1)
286 cp = find(world, target, targ.targetname); // get the previous corner first
287 cp = find(world, targetname, cp.target2); // now get its second target
290 objerror("train_next: no next target");
291 self.wait = targ.wait;
297 if (self.spawnflags & 1)
298 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, targ.speed, train_wait);
300 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
304 if (self.spawnflags & 1)
305 SUB_CalcMove_Bezier(cp.origin, targ.origin - self.mins, self.speed, train_wait);
307 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
311 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
314 void func_train_find()
317 targ = find(world, targetname, self.target);
318 self.target = targ.target;
320 objerror("func_train_find: no next target");
321 setorigin(self, targ.origin - self.mins);
322 self.nextthink = self.ltime + 1;
323 self.think = train_next;
326 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
327 Ridable platform, targets spawnfunc_path_corner path to follow.
328 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
329 target : targetname of first spawnfunc_path_corner (starts here)
331 void spawnfunc_func_train()
333 if (self.noise != "")
334 precache_sound(self.noise);
337 objerror("func_train without a target");
341 if not(InitMovingBrushTrigger())
343 self.effects |= EF_LOWPRECISION;
345 // wait for targets to spawn
346 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
348 self.blocked = generic_plat_blocked;
349 if(self.dmg & (!self.message))
350 self.message = " was squished";
351 if(self.dmg && (!self.message2))
352 self.message2 = "was squished by";
353 if(self.dmg && (!self.dmgtime))
355 self.dmgtime2 = time;
357 // TODO make a reset function for this one
360 void func_rotating_setactive(float astate)
363 if (astate == ACTIVE_TOGGLE)
365 if(self.active == ACTIVE_ACTIVE)
366 self.active = ACTIVE_NOT;
368 self.active = ACTIVE_ACTIVE;
371 self.active = astate;
373 if(self.active == ACTIVE_NOT)
374 self.avelocity = '0 0 0';
376 self.avelocity = self.pos1;
379 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
380 Brush model that spins in place on one axis (default Z).
381 speed : speed to rotate (in degrees per second)
382 noise : path/name of looping .wav file to play.
383 dmg : Do this mutch dmg every .dmgtime intervall when blocked
387 void spawnfunc_func_rotating()
389 if (self.noise != "")
391 precache_sound(self.noise);
392 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
395 self.active = ACTIVE_ACTIVE;
396 self.setactive = func_rotating_setactive;
400 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
401 if (self.spawnflags & 4) // X (untested)
402 self.avelocity = '0 0 1' * self.speed;
403 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
404 else if (self.spawnflags & 8) // Y (untested)
405 self.avelocity = '1 0 0' * self.speed;
406 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
408 self.avelocity = '0 1 0' * self.speed;
410 self.pos1 = self.avelocity;
412 if(self.dmg & (!self.message))
413 self.message = " was squished";
414 if(self.dmg && (!self.message2))
415 self.message2 = "was squished by";
418 if(self.dmg && (!self.dmgtime))
421 self.dmgtime2 = time;
423 if not(InitMovingBrushTrigger())
425 // no EF_LOWPRECISION here, as rounding angles is bad
427 self.blocked = generic_plat_blocked;
429 // wait for targets to spawn
430 self.nextthink = self.ltime + 999999999;
431 self.think = SUB_Null;
433 // TODO make a reset function for this one
437 void func_bobbing_controller_think()
440 self.nextthink = time + 0.1;
442 if not (self.owner.active == ACTIVE_ACTIVE)
444 self.owner.velocity = '0 0 0';
448 // calculate sinewave using makevectors
449 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
450 v = self.owner.destvec + self.owner.movedir * v_forward_y;
451 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
452 // * 10 so it will arrive in 0.1 sec
453 self.owner.velocity = (v - self.owner.origin) * 10;
456 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
457 Brush model that moves back and forth on one axis (default Z).
458 speed : how long one cycle takes in seconds (default 4)
459 height : how far the cycle moves (default 32)
460 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
461 noise : path/name of looping .wav file to play.
462 dmg : Do this mutch dmg every .dmgtime intervall when blocked
465 void spawnfunc_func_bobbing()
468 if (self.noise != "")
470 precache_sound(self.noise);
471 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
477 // center of bobbing motion
478 self.destvec = self.origin;
479 // time scale to get degrees
480 self.cnt = 360 / self.speed;
482 self.active = ACTIVE_ACTIVE;
484 // damage when blocked
485 self.blocked = generic_plat_blocked;
486 if(self.dmg & (!self.message))
487 self.message = " was squished";
488 if(self.dmg && (!self.message2))
489 self.message2 = "was squished by";
490 if(self.dmg && (!self.dmgtime))
492 self.dmgtime2 = time;
495 if (self.spawnflags & 1) // X
496 self.movedir = '1 0 0' * self.height;
497 else if (self.spawnflags & 2) // Y
498 self.movedir = '0 1 0' * self.height;
500 self.movedir = '0 0 1' * self.height;
502 if not(InitMovingBrushTrigger())
505 // wait for targets to spawn
506 controller = spawn();
507 controller.classname = "func_bobbing_controller";
508 controller.owner = self;
509 controller.nextthink = time + 1;
510 controller.think = func_bobbing_controller_think;
511 self.nextthink = self.ltime + 999999999;
512 self.think = SUB_Null;
514 // Savage: Reduce bandwith, critical on e.g. nexdm02
515 self.effects |= EF_LOWPRECISION;
517 // TODO make a reset function for this one
521 void func_pendulum_controller_think()
524 self.nextthink = time + 0.1;
526 if not (self.owner.active == ACTIVE_ACTIVE)
528 self.owner.avelocity_x = 0;
532 // calculate sinewave using makevectors
533 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
534 v = self.owner.speed * v_forward_y + self.cnt;
535 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
537 // * 10 so it will arrive in 0.1 sec
538 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
542 void spawnfunc_func_pendulum()
545 if (self.noise != "")
547 precache_sound(self.noise);
548 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
551 self.active = ACTIVE_ACTIVE;
553 // keys: angle, speed, phase, noise, freq
557 // not initializing self.dmg to 2, to allow damageless pendulum
559 if(self.dmg & (!self.message))
560 self.message = " was squished";
561 if(self.dmg && (!self.message2))
562 self.message2 = "was squished by";
563 if(self.dmg && (!self.dmgtime))
565 self.dmgtime2 = time;
567 self.blocked = generic_plat_blocked;
569 self.avelocity_z = 0.0000001;
570 if not(InitMovingBrushTrigger())
575 // find pendulum length (same formula as Q3A)
576 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
579 // copy initial angle
580 self.cnt = self.angles_z;
582 // wait for targets to spawn
583 controller = spawn();
584 controller.classname = "func_pendulum_controller";
585 controller.owner = self;
586 controller.nextthink = time + 1;
587 controller.think = func_pendulum_controller_think;
588 self.nextthink = self.ltime + 999999999;
589 self.think = SUB_Null;
591 //self.effects |= EF_LOWPRECISION;
593 // TODO make a reset function for this one
596 // button and multiple button
599 void() button_return;
603 self.state = STATE_TOP;
604 self.nextthink = self.ltime + self.wait;
605 self.think = button_return;
606 activator = self.enemy;
608 self.frame = 1; // use alternate textures
613 self.state = STATE_BOTTOM;
618 self.state = STATE_DOWN;
619 SUB_CalcMove (self.pos1, self.speed, button_done);
620 self.frame = 0; // use normal textures
622 self.takedamage = DAMAGE_YES; // can be shot again
626 void button_blocked()
628 // do nothing, just don't come all the way back out
634 self.health = self.max_health;
635 self.takedamage = DAMAGE_NO; // will be reset upon return
637 if (self.state == STATE_UP || self.state == STATE_TOP)
640 if (self.noise != "")
641 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
643 self.state = STATE_UP;
644 SUB_CalcMove (self.pos2, self.speed, button_wait);
649 self.health = self.max_health;
650 setorigin(self, self.pos1);
651 self.frame = 0; // use normal textures
652 self.state = STATE_BOTTOM;
654 self.takedamage = DAMAGE_YES; // can be shot again
659 // if (activator.classname != "player")
661 // dprint(activator.classname);
662 // dprint(" triggered a button\n");
665 if not (self.active == ACTIVE_ACTIVE)
668 self.enemy = activator;
674 // if (activator.classname != "player")
676 // dprint(activator.classname);
677 // dprint(" touched a button\n");
681 if not(other.iscreature)
683 if(other.velocity * self.movedir < 0)
687 self.enemy = other.owner;
691 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
693 if(self.spawnflags & DOOR_NOSPLASH)
694 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
696 self.health = self.health - damage;
697 if (self.health <= 0)
699 // if (activator.classname != "player")
701 // dprint(activator.classname);
702 // dprint(" killed a button\n");
704 self.enemy = damage_attacker;
710 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
711 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.
713 "angle" determines the opening direction
714 "target" all entities with a matching targetname will be used
715 "speed" override the default 40 speed
716 "wait" override the default 1 second wait (-1 = never return)
717 "lip" override the default 4 pixel lip remaining at end of move
718 "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
725 void spawnfunc_func_button()
729 if not(InitMovingBrushTrigger())
731 self.effects |= EF_LOWPRECISION;
733 self.blocked = button_blocked;
734 self.use = button_use;
736 // if (self.health == 0) // all buttons are now shootable
740 self.max_health = self.health;
741 self.event_damage = button_damage;
742 self.takedamage = DAMAGE_YES;
745 self.touch = button_touch;
755 precache_sound(self.noise);
757 self.active = ACTIVE_ACTIVE;
759 self.pos1 = self.origin;
760 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
761 self.flags |= FL_NOTARGET;
767 float DOOR_START_OPEN = 1;
768 float DOOR_DONT_LINK = 4;
769 float DOOR_TOGGLE = 32;
773 Doors are similar to buttons, but can spawn a fat trigger field around them
774 to open without a touch, and they link together to form simultanious
777 Door.owner is the master door. If there is only one door, it points to itself.
778 If multiple doors, all will point to a single one.
780 Door.enemy chains from the master door through all doors linked in the chain.
785 =============================================================================
789 =============================================================================
794 void() door_rotating_go_down;
795 void() door_rotating_go_up;
800 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
801 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
804 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
805 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
807 //Dont chamge direction for dead or dying stuff
808 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
811 if (self.state == STATE_DOWN)
812 if (self.classname == "door")
817 door_rotating_go_up ();
820 if (self.classname == "door")
825 door_rotating_go_down ();
829 //gib dying stuff just to make sure
830 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
831 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
835 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
836 // if a door has a negative wait, it would never come back if blocked,
837 // so let it just squash the object to death real fast
838 /* if (self.wait >= 0)
840 if (self.state == STATE_DOWN)
851 if (self.noise1 != "")
852 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
853 self.state = STATE_TOP;
854 if (self.spawnflags & DOOR_TOGGLE)
855 return; // don't come down automatically
856 if (self.classname == "door")
858 self.think = door_go_down;
861 self.think = door_rotating_go_down;
863 self.nextthink = self.ltime + self.wait;
866 void door_hit_bottom()
868 if (self.noise1 != "")
869 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
870 self.state = STATE_BOTTOM;
875 if (self.noise2 != "")
876 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
879 self.takedamage = DAMAGE_YES;
880 self.health = self.max_health;
883 self.state = STATE_DOWN;
884 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
889 if (self.state == STATE_UP)
890 return; // already going up
892 if (self.state == STATE_TOP)
893 { // reset top wait time
894 self.nextthink = self.ltime + self.wait;
898 if (self.noise2 != "")
899 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
900 self.state = STATE_UP;
901 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
904 oldmessage = self.message;
907 self.message = oldmessage;
913 =============================================================================
917 =============================================================================
920 float door_check_keys(void) {
930 if not(door.itemkeys)
933 // this door require a key
934 // only a player can have a key
935 if (other.classname != "player")
938 if (item_keys_usekey(door, other)) {
939 // some keys were used
940 if (other.key_door_messagetime <= time) {
941 play2(other, "misc/talk.wav");
942 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
943 other.key_door_messagetime = time + 2;
947 if (other.key_door_messagetime <= time) {
948 play2(other, "misc/talk.wav");
949 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
950 other.key_door_messagetime = time + 2;
955 // door is now unlocked
956 play2(other, "misc/talk.wav");
957 centerprint(other, "Door unlocked!");
969 if (self.owner != self)
970 objerror ("door_fire: self.owner != self");
974 if (self.spawnflags & DOOR_TOGGLE)
976 if (self.state == STATE_UP || self.state == STATE_TOP)
981 if (self.classname == "door")
987 door_rotating_go_down ();
990 } while ( (self != starte) && (self != world) );
996 // trigger all paired doors
1000 if (self.classname == "door")
1005 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1006 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1008 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1009 self.pos2 = '0 0 0' - self.pos2;
1011 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1012 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1013 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1015 door_rotating_go_up ();
1019 } while ( (self != starte) && (self != world) );
1028 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1040 void door_trigger_touch()
1042 if (other.health < 1)
1043 if not(other.iscreature && other.deadflag == DEAD_NO)
1046 if (time < self.attack_finished_single)
1049 // check if door is locked
1050 if (!door_check_keys())
1053 self.attack_finished_single = time + 1;
1062 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1065 if(self.spawnflags & DOOR_NOSPLASH)
1066 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1068 self.health = self.health - damage;
1070 if (self.itemkeys) {
1071 // don't allow opening doors through damage if keys are required
1075 if (self.health <= 0)
1079 self.health = self.max_health;
1080 self.takedamage = DAMAGE_NO; // wil be reset upon return
1096 if(other.classname != "player")
1098 if (self.owner.attack_finished_single > time)
1101 self.owner.attack_finished_single = time + 2;
1103 if (!(self.owner.dmg) && (self.owner.message != ""))
1105 if (other.flags & FL_CLIENT)
1106 centerprint (other, self.owner.message);
1107 play2(other, "misc/talk.wav");
1112 void door_generic_plat_blocked()
1115 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1116 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1119 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1120 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1122 //Dont chamge direction for dead or dying stuff
1123 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1126 if (self.state == STATE_DOWN)
1127 door_rotating_go_up ();
1129 door_rotating_go_down ();
1132 //gib dying stuff just to make sure
1133 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1134 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1138 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1139 // if a door has a negative wait, it would never come back if blocked,
1140 // so let it just squash the object to death real fast
1141 /* if (self.wait >= 0)
1143 if (self.state == STATE_DOWN)
1144 door_rotating_go_up ();
1146 door_rotating_go_down ();
1152 void door_rotating_hit_top()
1154 if (self.noise1 != "")
1155 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1156 self.state = STATE_TOP;
1157 if (self.spawnflags & DOOR_TOGGLE)
1158 return; // don't come down automatically
1159 self.think = door_rotating_go_down;
1160 self.nextthink = self.ltime + self.wait;
1163 void door_rotating_hit_bottom()
1165 if (self.noise1 != "")
1166 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1167 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1169 self.pos2 = '0 0 0' - self.pos2;
1172 self.state = STATE_BOTTOM;
1175 void door_rotating_go_down()
1177 if (self.noise2 != "")
1178 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1179 if (self.max_health)
1181 self.takedamage = DAMAGE_YES;
1182 self.health = self.max_health;
1185 self.state = STATE_DOWN;
1186 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1189 void door_rotating_go_up()
1191 if (self.state == STATE_UP)
1192 return; // already going up
1194 if (self.state == STATE_TOP)
1195 { // reset top wait time
1196 self.nextthink = self.ltime + self.wait;
1199 if (self.noise2 != "")
1200 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1201 self.state = STATE_UP;
1202 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1205 oldmessage = self.message;
1208 self.message = oldmessage;
1215 =============================================================================
1219 =============================================================================
1223 entity spawn_field(vector fmins, vector fmaxs)
1229 trigger.classname = "doortriggerfield";
1230 trigger.movetype = MOVETYPE_NONE;
1231 trigger.solid = SOLID_TRIGGER;
1232 trigger.owner = self;
1233 trigger.touch = door_trigger_touch;
1237 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1242 float EntitiesTouching(entity e1, entity e2)
1244 if (e1.absmin_x > e2.absmax_x)
1246 if (e1.absmin_y > e2.absmax_y)
1248 if (e1.absmin_z > e2.absmax_z)
1250 if (e1.absmax_x < e2.absmin_x)
1252 if (e1.absmax_y < e2.absmin_y)
1254 if (e1.absmax_z < e2.absmin_z)
1270 vector cmins, cmaxs;
1273 return; // already linked by another door
1274 if (self.spawnflags & 4)
1276 self.owner = self.enemy = self;
1284 self.trigger_field = spawn_field(self.absmin, self.absmax);
1286 return; // don't want to link this door
1289 cmins = self.absmin;
1290 cmaxs = self.absmax;
1297 self.owner = starte; // master door
1300 starte.health = self.health;
1302 starte.targetname = self.targetname;
1303 if (self.message != "")
1304 starte.message = self.message;
1306 t = find(t, classname, self.classname);
1309 self.enemy = starte; // make the chain a loop
1311 // shootable, or triggered doors just needed the owner/enemy links,
1312 // they don't spawn a field
1323 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1328 if (EntitiesTouching(self,t))
1331 objerror ("cross connected doors");
1336 if (t.absmin_x < cmins_x)
1337 cmins_x = t.absmin_x;
1338 if (t.absmin_y < cmins_y)
1339 cmins_y = t.absmin_y;
1340 if (t.absmin_z < cmins_z)
1341 cmins_z = t.absmin_z;
1342 if (t.absmax_x > cmaxs_x)
1343 cmaxs_x = t.absmax_x;
1344 if (t.absmax_y > cmaxs_y)
1345 cmaxs_y = t.absmax_y;
1346 if (t.absmax_z > cmaxs_z)
1347 cmaxs_z = t.absmax_z;
1354 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1355 if two doors touch, they are assumed to be connected and operate as a unit.
1357 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1359 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).
1361 GOLD_KEY causes the door to open only if the activator holds a gold key.
1363 SILVER_KEY causes the door to open only if the activator holds a silver key.
1365 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1366 "angle" determines the opening direction
1367 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1368 "health" if set, door must be shot open
1369 "speed" movement speed (100 default)
1370 "wait" wait before returning (3 default, -1 = never return)
1371 "lip" lip remaining at end of move (8 default)
1372 "dmg" damage to inflict when blocked (2 default)
1379 FIXME: only one sound set available at the time being
1383 void door_init_startopen()
1385 setorigin (self, self.pos2);
1386 self.pos2 = self.pos1;
1387 self.pos1 = self.origin;
1392 setorigin(self, self.pos1);
1393 self.velocity = '0 0 0';
1394 self.state = STATE_BOTTOM;
1395 self.think = SUB_Null;
1398 // spawnflags require key (for now only func_door)
1399 #define SPAWNFLAGS_GOLD_KEY 8
1400 #define SPAWNFLAGS_SILVER_KEY 16
1401 void spawnfunc_func_door()
1403 // Quake 1 keys compatibility
1404 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1405 self.itemkeys |= ITEM_KEY_BIT(0);
1406 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1407 self.itemkeys |= ITEM_KEY_BIT(1);
1409 //if (!self.deathtype) // map makers can override this
1410 // self.deathtype = " got in the way";
1413 self.max_health = self.health;
1414 if not(InitMovingBrushTrigger())
1416 self.effects |= EF_LOWPRECISION;
1417 self.classname = "door";
1419 self.blocked = door_blocked;
1420 self.use = door_use;
1422 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1423 // if(self.spawnflags & 8)
1424 // self.dmg = 10000;
1426 if(self.dmg && (!self.message))
1427 self.message = "was squished";
1428 if(self.dmg && (!self.message2))
1429 self.message2 = "was squished by";
1431 if (self.sounds > 0)
1433 precache_sound ("plats/medplat1.wav");
1434 precache_sound ("plats/medplat2.wav");
1435 self.noise2 = "plats/medplat1.wav";
1436 self.noise1 = "plats/medplat2.wav";
1446 self.pos1 = self.origin;
1447 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1449 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1450 // but spawn in the open position
1451 if (self.spawnflags & DOOR_START_OPEN)
1452 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1454 self.state = STATE_BOTTOM;
1458 self.takedamage = DAMAGE_YES;
1459 self.event_damage = door_damage;
1465 self.touch = door_touch;
1467 // LinkDoors can't be done until all of the doors have been spawned, so
1468 // the sizes can be detected properly.
1469 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1471 self.reset = door_reset;
1474 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1475 if two doors touch, they are assumed to be connected and operate as a unit.
1477 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1479 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1480 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1481 must have set trigger_reverse to 1.
1482 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1484 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).
1486 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1487 "angle" determines the destination angle for opening. negative values reverse the direction.
1488 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1489 "health" if set, door must be shot open
1490 "speed" movement speed (100 default)
1491 "wait" wait before returning (3 default, -1 = never return)
1492 "dmg" damage to inflict when blocked (2 default)
1499 FIXME: only one sound set available at the time being
1502 void door_rotating_reset()
1504 self.angles = self.pos1;
1505 self.avelocity = '0 0 0';
1506 self.state = STATE_BOTTOM;
1507 self.think = SUB_Null;
1510 void door_rotating_init_startopen()
1512 self.angles = self.movedir;
1513 self.pos2 = '0 0 0';
1514 self.pos1 = self.movedir;
1518 void spawnfunc_func_door_rotating()
1521 //if (!self.deathtype) // map makers can override this
1522 // self.deathtype = " got in the way";
1524 // I abuse "movedir" for denoting the axis for now
1525 if (self.spawnflags & 64) // X (untested)
1526 self.movedir = '0 0 1';
1527 else if (self.spawnflags & 128) // Y (untested)
1528 self.movedir = '1 0 0';
1530 self.movedir = '0 1 0';
1532 if (self.angles_y==0) self.angles_y = 90;
1534 self.movedir = self.movedir * self.angles_y;
1535 self.angles = '0 0 0';
1537 self.max_health = self.health;
1538 self.avelocity = self.movedir;
1539 if not(InitMovingBrushTrigger())
1541 self.velocity = '0 0 0';
1542 //self.effects |= EF_LOWPRECISION;
1543 self.classname = "door_rotating";
1545 self.blocked = door_blocked;
1546 self.use = door_use;
1548 if(self.spawnflags & 8)
1551 if(self.dmg && (!self.message))
1552 self.message = "was squished";
1553 if(self.dmg && (!self.message2))
1554 self.message2 = "was squished by";
1556 if (self.sounds > 0)
1558 precache_sound ("plats/medplat1.wav");
1559 precache_sound ("plats/medplat2.wav");
1560 self.noise2 = "plats/medplat1.wav";
1561 self.noise1 = "plats/medplat2.wav";
1568 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1570 self.pos1 = '0 0 0';
1571 self.pos2 = self.movedir;
1573 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1574 // but spawn in the open position
1575 if (self.spawnflags & DOOR_START_OPEN)
1576 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1578 self.state = STATE_BOTTOM;
1582 self.takedamage = DAMAGE_YES;
1583 self.event_damage = door_damage;
1589 self.touch = door_touch;
1591 // LinkDoors can't be done until all of the doors have been spawned, so
1592 // the sizes can be detected properly.
1593 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1595 self.reset = door_rotating_reset;
1599 =============================================================================
1603 =============================================================================
1606 void() fd_secret_move1;
1607 void() fd_secret_move2;
1608 void() fd_secret_move3;
1609 void() fd_secret_move4;
1610 void() fd_secret_move5;
1611 void() fd_secret_move6;
1612 void() fd_secret_done;
1614 float SECRET_OPEN_ONCE = 1; // stays open
1615 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1616 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1617 float SECRET_NO_SHOOT = 8; // only opened by trigger
1618 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1621 void fd_secret_use()
1624 string message_save;
1626 self.health = 10000;
1627 self.bot_attack = TRUE;
1629 // exit if still moving around...
1630 if (self.origin != self.oldorigin)
1633 message_save = self.message;
1634 self.message = ""; // no more message
1635 SUB_UseTargets(); // fire all targets / killtargets
1636 self.message = message_save;
1638 self.velocity = '0 0 0';
1640 // Make a sound, wait a little...
1642 if (self.noise1 != "")
1643 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1644 self.nextthink = self.ltime + 0.1;
1646 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1647 makevectors(self.mangle);
1651 if (self.spawnflags & SECRET_1ST_DOWN)
1652 self.t_width = fabs(v_up * self.size);
1654 self.t_width = fabs(v_right * self.size);
1658 self.t_length = fabs(v_forward * self.size);
1660 if (self.spawnflags & SECRET_1ST_DOWN)
1661 self.dest1 = self.origin - v_up * self.t_width;
1663 self.dest1 = self.origin + v_right * (self.t_width * temp);
1665 self.dest2 = self.dest1 + v_forward * self.t_length;
1666 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1667 if (self.noise2 != "")
1668 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1671 // Wait after first movement...
1672 void fd_secret_move1()
1674 self.nextthink = self.ltime + 1.0;
1675 self.think = fd_secret_move2;
1676 if (self.noise3 != "")
1677 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1680 // Start moving sideways w/sound...
1681 void fd_secret_move2()
1683 if (self.noise2 != "")
1684 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1685 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1688 // Wait here until time to go back...
1689 void fd_secret_move3()
1691 if (self.noise3 != "")
1692 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1693 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1695 self.nextthink = self.ltime + self.wait;
1696 self.think = fd_secret_move4;
1701 void fd_secret_move4()
1703 if (self.noise2 != "")
1704 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1705 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1709 void fd_secret_move5()
1711 self.nextthink = self.ltime + 1.0;
1712 self.think = fd_secret_move6;
1713 if (self.noise3 != "")
1714 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1717 void fd_secret_move6()
1719 if (self.noise2 != "")
1720 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1721 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1724 void fd_secret_done()
1726 if (self.spawnflags&SECRET_YES_SHOOT)
1728 self.health = 10000;
1729 self.takedamage = DAMAGE_YES;
1730 //self.th_pain = fd_secret_use;
1732 if (self.noise3 != "")
1733 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1736 void secret_blocked()
1738 if (time < self.attack_finished_single)
1740 self.attack_finished_single = time + 0.5;
1741 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1753 if not(other.iscreature)
1755 if (self.attack_finished_single > time)
1758 self.attack_finished_single = time + 2;
1762 if (other.flags & FL_CLIENT)
1763 centerprint (other, self.message);
1764 play2(other, "misc/talk.wav");
1770 if (self.spawnflags&SECRET_YES_SHOOT)
1772 self.health = 10000;
1773 self.takedamage = DAMAGE_YES;
1775 setorigin(self, self.oldorigin);
1776 self.think = SUB_Null;
1779 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1780 Basic secret door. Slides back, then to the side. Angle determines direction.
1781 wait = # of seconds before coming back
1782 1st_left = 1st move is left of arrow
1783 1st_down = 1st move is down from arrow
1784 always_shoot = even if targeted, keep shootable
1785 t_width = override WIDTH to move back (or height if going down)
1786 t_length = override LENGTH to move sideways
1787 "dmg" damage to inflict when blocked (2 default)
1789 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1796 void spawnfunc_func_door_secret()
1798 /*if (!self.deathtype) // map makers can override this
1799 self.deathtype = " got in the way";*/
1805 self.mangle = self.angles;
1806 self.angles = '0 0 0';
1807 self.classname = "door";
1808 if not(InitMovingBrushTrigger())
1810 self.effects |= EF_LOWPRECISION;
1812 self.touch = secret_touch;
1813 self.blocked = secret_blocked;
1815 self.use = fd_secret_use;
1820 self.spawnflags |= SECRET_YES_SHOOT;
1822 if(self.spawnflags&SECRET_YES_SHOOT)
1824 self.health = 10000;
1825 self.takedamage = DAMAGE_YES;
1826 self.event_damage = fd_secret_use;
1828 self.oldorigin = self.origin;
1830 self.wait = 5; // 5 seconds before closing
1832 self.reset = secret_reset;
1836 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1837 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1838 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
1839 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1840 height: amplitude modifier (default 32)
1841 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1842 noise: path/name of looping .wav file to play.
1843 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1847 void func_fourier_controller_think()
1852 self.nextthink = time + 0.1;
1853 if not (self.owner.active == ACTIVE_ACTIVE)
1855 self.owner.velocity = '0 0 0';
1860 n = floor((tokenize_console(self.owner.netname)) / 5);
1861 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1863 v = self.owner.destvec;
1865 for(i = 0; i < n; ++i)
1867 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1868 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;
1871 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1872 // * 10 so it will arrive in 0.1 sec
1873 self.owner.velocity = (v - self.owner.origin) * 10;
1876 void spawnfunc_func_fourier()
1879 if (self.noise != "")
1881 precache_sound(self.noise);
1882 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1889 self.destvec = self.origin;
1890 self.cnt = 360 / self.speed;
1892 self.blocked = generic_plat_blocked;
1893 if(self.dmg & (!self.message))
1894 self.message = " was squished";
1895 if(self.dmg && (!self.message2))
1896 self.message2 = "was squished by";
1897 if(self.dmg && (!self.dmgtime))
1898 self.dmgtime = 0.25;
1899 self.dmgtime2 = time;
1901 if(self.netname == "")
1902 self.netname = "1 0 0 0 1";
1904 if not(InitMovingBrushTrigger())
1907 self.active = ACTIVE_ACTIVE;
1909 // wait for targets to spawn
1910 controller = spawn();
1911 controller.classname = "func_fourier_controller";
1912 controller.owner = self;
1913 controller.nextthink = time + 1;
1914 controller.think = func_fourier_controller_think;
1915 self.nextthink = self.ltime + 999999999;
1916 self.think = SUB_Null;
1918 // Savage: Reduce bandwith, critical on e.g. nexdm02
1919 self.effects |= EF_LOWPRECISION;
1921 // TODO make a reset function for this one
1924 // reusing some fields havocbots declared
1925 .entity wp00, wp01, wp02, wp03;
1927 .float targetfactor, target2factor, target3factor, target4factor;
1928 .vector targetnormal, target2normal, target3normal, target4normal;
1930 vector func_vectormamamam_origin(entity o, float t)
1942 p = e.origin + t * e.velocity;
1944 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1946 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1952 p = e.origin + t * e.velocity;
1954 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1956 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1962 p = e.origin + t * e.velocity;
1964 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1966 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1972 p = e.origin + t * e.velocity;
1974 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1976 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1982 void func_vectormamamam_controller_think()
1984 self.nextthink = time + 0.1;
1986 if not (self.owner.active == ACTIVE_ACTIVE)
1988 self.owner.velocity = '0 0 0';
1992 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1993 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1996 void func_vectormamamam_findtarget()
1998 if(self.target != "")
1999 self.wp00 = find(world, targetname, self.target);
2001 if(self.target2 != "")
2002 self.wp01 = find(world, targetname, self.target2);
2004 if(self.target3 != "")
2005 self.wp02 = find(world, targetname, self.target3);
2007 if(self.target4 != "")
2008 self.wp03 = find(world, targetname, self.target4);
2010 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2011 objerror("No reference entity found, so there is nothing to move. Aborting.");
2013 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2016 controller = spawn();
2017 controller.classname = "func_vectormamamam_controller";
2018 controller.owner = self;
2019 controller.nextthink = time + 1;
2020 controller.think = func_vectormamamam_controller_think;
2023 void spawnfunc_func_vectormamamam()
2025 if (self.noise != "")
2027 precache_sound(self.noise);
2028 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2031 if(!self.targetfactor)
2032 self.targetfactor = 1;
2034 if(!self.target2factor)
2035 self.target2factor = 1;
2037 if(!self.target3factor)
2038 self.target3factor = 1;
2040 if(!self.target4factor)
2041 self.target4factor = 1;
2043 if(vlen(self.targetnormal))
2044 self.targetnormal = normalize(self.targetnormal);
2046 if(vlen(self.target2normal))
2047 self.target2normal = normalize(self.target2normal);
2049 if(vlen(self.target3normal))
2050 self.target3normal = normalize(self.target3normal);
2052 if(vlen(self.target4normal))
2053 self.target4normal = normalize(self.target4normal);
2055 self.blocked = generic_plat_blocked;
2056 if(self.dmg & (!self.message))
2057 self.message = " was squished";
2058 if(self.dmg && (!self.message2))
2059 self.message2 = "was squished by";
2060 if(self.dmg && (!self.dmgtime))
2061 self.dmgtime = 0.25;
2062 self.dmgtime2 = time;
2064 if(self.netname == "")
2065 self.netname = "1 0 0 0 1";
2067 if not(InitMovingBrushTrigger())
2070 // wait for targets to spawn
2071 self.nextthink = self.ltime + 999999999;
2072 self.think = SUB_Null;
2074 // Savage: Reduce bandwith, critical on e.g. nexdm02
2075 self.effects |= EF_LOWPRECISION;
2077 self.active = ACTIVE_ACTIVE;
2079 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2082 void conveyor_think()
2086 // set myself as current conveyor where possible
2087 for(e = world; (e = findentity(e, conveyor, self)); )
2092 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2093 if(!e.conveyor.state)
2096 vector emin = e.absmin;
2097 vector emax = e.absmax;
2098 if(self.solid == SOLID_BSP)
2103 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2104 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2108 for(e = world; (e = findentity(e, conveyor, self)); )
2110 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2111 continue; // done in SV_PlayerPhysics
2113 setorigin(e, e.origin + self.movedir * sys_frametime);
2114 move_out_of_solid(e);
2115 UpdateCSQCProjectile(e);
2117 // stupid conveyor code
2118 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2119 if(trace_fraction > 0)
2120 setorigin(e, trace_endpos);
2125 self.nextthink = time;
2130 self.state = !self.state;
2133 void conveyor_reset()
2135 self.state = (self.spawnflags & 1);
2138 void conveyor_init()
2142 self.movedir = self.movedir * self.speed;
2143 self.think = conveyor_think;
2144 self.nextthink = time;
2147 self.use = conveyor_use;
2148 self.reset = conveyor_reset;
2155 void spawnfunc_trigger_conveyor()
2162 void spawnfunc_func_conveyor()
2165 InitMovingBrushTrigger();
2166 self.movetype = MOVETYPE_NONE;