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');
17 .entity trigger_field;
19 void() plat_center_touch;
20 void() plat_outside_touch;
21 void() plat_trigger_use;
25 const float PLAT_LOW_TRIGGER = 1;
27 void plat_spawn_inside_trigger()
33 trigger.touch = plat_center_touch;
34 trigger.movetype = MOVETYPE_NONE;
35 trigger.solid = SOLID_TRIGGER;
38 tmin = self.absmin + '25 25 0';
39 tmax = self.absmax - '25 25 -8';
40 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
41 if (self.spawnflags & PLAT_LOW_TRIGGER)
44 if (self.size_x <= 50)
46 tmin_x = (self.mins_x + self.maxs_x) / 2;
49 if (self.size_y <= 50)
51 tmin_y = (self.mins_y + self.maxs_y) / 2;
59 setsize (trigger, tmin, tmax);
63 // otherwise, something is fishy...
65 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
70 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
72 self.think = plat_go_down;
73 self.nextthink = self.ltime + 3;
76 void plat_hit_bottom()
78 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
84 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
86 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
91 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
93 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
96 void plat_center_touch()
98 if not(other.iscreature)
101 if (other.health <= 0)
107 else if (self.state == 1)
108 self.nextthink = self.ltime + 1; // delay going down
111 void plat_outside_touch()
113 if not(other.iscreature)
116 if (other.health <= 0)
124 void plat_trigger_use()
127 return; // already activated
134 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
135 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
137 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
138 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
139 // Gib dead/dying stuff
140 if(other.deadflag != DEAD_NO)
141 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
146 else if (self.state == 3)
148 // when in other states, then the plat_crush event came delayed after
149 // plat state already had changed
150 // this isn't a bug per se!
156 self.use = func_null;
158 objerror ("plat_use: not in up state");
162 .string sound1, sound2;
168 setorigin (self, self.pos1);
174 setorigin (self, self.pos2);
176 self.use = plat_trigger_use;
180 void spawnfunc_path_corner() { }
181 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;
237 self.height = self.size_z - self.lip;
239 self.pos1 = self.origin;
240 self.pos2 = self.origin;
241 self.pos2_z = self.origin_z - self.height;
243 self.reset = plat_reset;
246 plat_spawn_inside_trigger (); // the "start moving" trigger
254 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
262 self.think = train_next;
263 self.nextthink = self.ltime + self.wait;
277 targ = find(world, targetname, self.target);
279 self.target = targ.target;
280 if (self.target == "")
281 objerror("train_next: no next target");
282 self.wait = targ.wait;
287 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
289 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
292 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
295 void func_train_find()
298 targ = find(world, targetname, self.target);
299 self.target = targ.target;
300 if (self.target == "")
301 objerror("func_train_find: no next target");
302 setorigin(self, targ.origin - self.mins);
303 self.nextthink = self.ltime + 1;
304 self.think = train_next;
307 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
308 Ridable platform, targets spawnfunc_path_corner path to follow.
309 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
310 target : targetname of first spawnfunc_path_corner (starts here)
312 void spawnfunc_func_train()
314 if (self.noise != "")
315 precache_sound(self.noise);
317 if (self.target == "")
318 objerror("func_train without a target");
322 if not(InitMovingBrushTrigger())
324 self.effects |= EF_LOWPRECISION;
326 // wait for targets to spawn
327 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
329 self.blocked = generic_plat_blocked;
330 if(self.dmg && (self.message == ""))
331 self.message = " was squished";
332 if(self.dmg && (self.message2 == ""))
333 self.message2 = "was squished by";
334 if(self.dmg && (!self.dmgtime))
336 self.dmgtime2 = time;
338 // TODO make a reset function for this one
341 void func_rotating_setactive(float astate)
344 if (astate == ACTIVE_TOGGLE)
346 if(self.active == ACTIVE_ACTIVE)
347 self.active = ACTIVE_NOT;
349 self.active = ACTIVE_ACTIVE;
352 self.active = astate;
354 if(self.active == ACTIVE_NOT)
355 self.avelocity = '0 0 0';
357 self.avelocity = self.pos1;
360 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
361 Brush model that spins in place on one axis (default Z).
362 speed : speed to rotate (in degrees per second)
363 noise : path/name of looping .wav file to play.
364 dmg : Do this mutch dmg every .dmgtime intervall when blocked
368 void spawnfunc_func_rotating()
370 if (self.noise != "")
372 precache_sound(self.noise);
373 ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE);
376 self.active = ACTIVE_ACTIVE;
377 self.setactive = func_rotating_setactive;
381 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
382 if (self.spawnflags & 4) // X (untested)
383 self.avelocity = '0 0 1' * self.speed;
384 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
385 else if (self.spawnflags & 8) // Y (untested)
386 self.avelocity = '1 0 0' * self.speed;
387 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
389 self.avelocity = '0 1 0' * self.speed;
391 self.pos1 = self.avelocity;
393 if(self.dmg && (self.message == ""))
394 self.message = " was squished";
395 if(self.dmg && (self.message2 == ""))
396 self.message2 = "was squished by";
399 if(self.dmg && (!self.dmgtime))
402 self.dmgtime2 = time;
404 if not(InitMovingBrushTrigger())
406 // no EF_LOWPRECISION here, as rounding angles is bad
408 self.blocked = generic_plat_blocked;
410 // wait for targets to spawn
411 self.nextthink = self.ltime + 999999999;
412 self.think = SUB_NullThink; // for PushMove
414 // TODO make a reset function for this one
418 void func_bobbing_controller_think()
421 self.nextthink = time + 0.1;
423 if not (self.owner.active == ACTIVE_ACTIVE)
425 self.owner.velocity = '0 0 0';
429 // calculate sinewave using makevectors
430 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
431 v = self.owner.destvec + self.owner.movedir * v_forward_y;
432 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
433 // * 10 so it will arrive in 0.1 sec
434 self.owner.velocity = (v - self.owner.origin) * 10;
437 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
438 Brush model that moves back and forth on one axis (default Z).
439 speed : how long one cycle takes in seconds (default 4)
440 height : how far the cycle moves (default 32)
441 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
442 noise : path/name of looping .wav file to play.
443 dmg : Do this mutch dmg every .dmgtime intervall when blocked
446 void spawnfunc_func_bobbing()
449 if (self.noise != "")
451 precache_sound(self.noise);
452 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
458 // center of bobbing motion
459 self.destvec = self.origin;
460 // time scale to get degrees
461 self.cnt = 360 / self.speed;
463 self.active = ACTIVE_ACTIVE;
465 // damage when blocked
466 self.blocked = generic_plat_blocked;
467 if(self.dmg && (self.message == ""))
468 self.message = " was squished";
469 if(self.dmg && (self.message2 == ""))
470 self.message2 = "was squished by";
471 if(self.dmg && (!self.dmgtime))
473 self.dmgtime2 = time;
476 if (self.spawnflags & 1) // X
477 self.movedir = '1 0 0' * self.height;
478 else if (self.spawnflags & 2) // Y
479 self.movedir = '0 1 0' * self.height;
481 self.movedir = '0 0 1' * self.height;
483 if not(InitMovingBrushTrigger())
486 // wait for targets to spawn
487 controller = spawn();
488 controller.classname = "func_bobbing_controller";
489 controller.owner = self;
490 controller.nextthink = time + 1;
491 controller.think = func_bobbing_controller_think;
492 self.nextthink = self.ltime + 999999999;
493 self.think = SUB_NullThink; // for PushMove
495 // Savage: Reduce bandwith, critical on e.g. nexdm02
496 self.effects |= EF_LOWPRECISION;
498 // TODO make a reset function for this one
502 void func_pendulum_controller_think()
505 self.nextthink = time + 0.1;
507 if not (self.owner.active == ACTIVE_ACTIVE)
509 self.owner.avelocity_x = 0;
513 // calculate sinewave using makevectors
514 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
515 v = self.owner.speed * v_forward_y + self.cnt;
516 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
518 // * 10 so it will arrive in 0.1 sec
519 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
523 void spawnfunc_func_pendulum()
526 if (self.noise != "")
528 precache_sound(self.noise);
529 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
532 self.active = ACTIVE_ACTIVE;
534 // keys: angle, speed, phase, noise, freq
538 // not initializing self.dmg to 2, to allow damageless pendulum
540 if(self.dmg && (self.message == ""))
541 self.message = " was squished";
542 if(self.dmg && (self.message2 == ""))
543 self.message2 = "was squished by";
544 if(self.dmg && (!self.dmgtime))
546 self.dmgtime2 = time;
548 self.blocked = generic_plat_blocked;
550 self.avelocity_z = 0.0000001;
551 if not(InitMovingBrushTrigger())
556 // find pendulum length (same formula as Q3A)
557 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
560 // copy initial angle
561 self.cnt = self.angles_z;
563 // wait for targets to spawn
564 controller = spawn();
565 controller.classname = "func_pendulum_controller";
566 controller.owner = self;
567 controller.nextthink = time + 1;
568 controller.think = func_pendulum_controller_think;
569 self.nextthink = self.ltime + 999999999;
570 self.think = SUB_NullThink; // for PushMove
572 //self.effects |= EF_LOWPRECISION;
574 // TODO make a reset function for this one
577 // button and multiple button
580 void() button_return;
584 self.state = STATE_TOP;
585 self.nextthink = self.ltime + self.wait;
586 self.think = button_return;
587 activator = self.enemy;
589 self.frame = 1; // use alternate textures
594 self.state = STATE_BOTTOM;
599 self.state = STATE_DOWN;
600 SUB_CalcMove (self.pos1, self.speed, button_done);
601 self.frame = 0; // use normal textures
603 self.takedamage = DAMAGE_YES; // can be shot again
607 void button_blocked()
609 // do nothing, just don't come all the way back out
615 self.health = self.max_health;
616 self.takedamage = DAMAGE_NO; // will be reset upon return
618 if (self.state == STATE_UP || self.state == STATE_TOP)
621 if (self.noise != "")
622 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
624 self.state = STATE_UP;
625 SUB_CalcMove (self.pos2, self.speed, button_wait);
630 self.health = self.max_health;
631 setorigin(self, self.pos1);
632 self.frame = 0; // use normal textures
633 self.state = STATE_BOTTOM;
635 self.takedamage = DAMAGE_YES; // can be shot again
640 if not (self.active == ACTIVE_ACTIVE)
643 self.enemy = activator;
651 if not(other.iscreature)
653 if(other.velocity * self.movedir < 0)
657 self.enemy = other.owner;
661 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
663 if(self.spawnflags & DOOR_NOSPLASH)
664 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
666 self.health = self.health - damage;
667 if (self.health <= 0)
669 self.enemy = damage_attacker;
675 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
676 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.
678 "angle" determines the opening direction
679 "target" all entities with a matching targetname will be used
680 "speed" override the default 40 speed
681 "wait" override the default 1 second wait (-1 = never return)
682 "lip" override the default 4 pixel lip remaining at end of move
683 "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
690 void spawnfunc_func_button()
694 if not(InitMovingBrushTrigger())
696 self.effects |= EF_LOWPRECISION;
698 self.blocked = button_blocked;
699 self.use = button_use;
701 // if (self.health == 0) // all buttons are now shootable
705 self.max_health = self.health;
706 self.event_damage = button_damage;
707 self.takedamage = DAMAGE_YES;
710 self.touch = button_touch;
720 precache_sound(self.noise);
722 self.active = ACTIVE_ACTIVE;
724 self.pos1 = self.origin;
725 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
726 self.flags |= FL_NOTARGET;
732 const float DOOR_START_OPEN = 1;
733 const float DOOR_DONT_LINK = 4;
734 const float DOOR_TOGGLE = 32;
738 Doors are similar to buttons, but can spawn a fat trigger field around them
739 to open without a touch, and they link together to form simultanious
742 Door.owner is the master door. If there is only one door, it points to itself.
743 If multiple doors, all will point to a single one.
745 Door.enemy chains from the master door through all doors linked in the chain.
750 =============================================================================
754 =============================================================================
759 void() door_rotating_go_down;
760 void() door_rotating_go_up;
765 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
766 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
769 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
770 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
772 //Dont chamge direction for dead or dying stuff
773 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
776 if (self.state == STATE_DOWN)
777 if (self.classname == "door")
782 door_rotating_go_up ();
785 if (self.classname == "door")
790 door_rotating_go_down ();
794 //gib dying stuff just to make sure
795 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
796 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
800 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
801 // if a door has a negative wait, it would never come back if blocked,
802 // so let it just squash the object to death real fast
803 /* if (self.wait >= 0)
805 if (self.state == STATE_DOWN)
816 if (self.noise1 != "")
817 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
818 self.state = STATE_TOP;
819 if (self.spawnflags & DOOR_TOGGLE)
820 return; // don't come down automatically
821 if (self.classname == "door")
823 self.think = door_go_down;
826 self.think = door_rotating_go_down;
828 self.nextthink = self.ltime + self.wait;
831 void door_hit_bottom()
833 if (self.noise1 != "")
834 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
835 self.state = STATE_BOTTOM;
840 if (self.noise2 != "")
841 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
844 self.takedamage = DAMAGE_YES;
845 self.health = self.max_health;
848 self.state = STATE_DOWN;
849 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
854 if (self.state == STATE_UP)
855 return; // already going up
857 if (self.state == STATE_TOP)
858 { // reset top wait time
859 self.nextthink = self.ltime + self.wait;
863 if (self.noise2 != "")
864 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
865 self.state = STATE_UP;
866 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
869 oldmessage = self.message;
872 self.message = oldmessage;
878 =============================================================================
882 =============================================================================
885 float door_check_keys(void) {
895 if not(door.itemkeys)
898 // this door require a key
899 // only a player can have a key
900 if not(IS_PLAYER(other))
903 if (item_keys_usekey(door, other)) {
904 // some keys were used
905 if (other.key_door_messagetime <= time) {
906 play2(other, "misc/talk.wav");
907 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
908 other.key_door_messagetime = time + 2;
912 if (other.key_door_messagetime <= time) {
913 play2(other, "misc/talk.wav");
914 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
915 other.key_door_messagetime = time + 2;
920 // door is now unlocked
921 play2(other, "misc/talk.wav");
922 centerprint(other, "Door unlocked!");
934 if (self.owner != self)
935 objerror ("door_fire: self.owner != self");
939 if (self.spawnflags & DOOR_TOGGLE)
941 if (self.state == STATE_UP || self.state == STATE_TOP)
946 if (self.classname == "door")
952 door_rotating_go_down ();
955 } while ( (self != starte) && (self != world) );
961 // trigger all paired doors
965 if (self.classname == "door")
970 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
971 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
973 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
974 self.pos2 = '0 0 0' - self.pos2;
976 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
977 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
978 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
980 door_rotating_go_up ();
984 } while ( (self != starte) && (self != world) );
993 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1005 void door_trigger_touch()
1007 if (other.health < 1)
1008 if not(other.iscreature && other.deadflag == DEAD_NO)
1011 if (time < self.attack_finished_single)
1014 // check if door is locked
1015 if (!door_check_keys())
1018 self.attack_finished_single = time + 1;
1027 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1030 if(self.spawnflags & DOOR_NOSPLASH)
1031 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1033 self.health = self.health - damage;
1035 if (self.itemkeys) {
1036 // don't allow opening doors through damage if keys are required
1040 if (self.health <= 0)
1044 self.health = self.max_health;
1045 self.takedamage = DAMAGE_NO; // wil be reset upon return
1061 if not(IS_PLAYER(other))
1063 if (self.owner.attack_finished_single > time)
1066 self.owner.attack_finished_single = time + 2;
1068 if (!(self.owner.dmg) && (self.owner.message != ""))
1070 if (IS_CLIENT(other))
1071 centerprint (other, self.owner.message);
1072 play2(other, "misc/talk.wav");
1077 void door_generic_plat_blocked()
1080 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1081 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1084 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1085 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1087 //Dont chamge direction for dead or dying stuff
1088 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1091 if (self.state == STATE_DOWN)
1092 door_rotating_go_up ();
1094 door_rotating_go_down ();
1097 //gib dying stuff just to make sure
1098 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1099 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1103 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1104 // if a door has a negative wait, it would never come back if blocked,
1105 // so let it just squash the object to death real fast
1106 /* if (self.wait >= 0)
1108 if (self.state == STATE_DOWN)
1109 door_rotating_go_up ();
1111 door_rotating_go_down ();
1117 void door_rotating_hit_top()
1119 if (self.noise1 != "")
1120 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1121 self.state = STATE_TOP;
1122 if (self.spawnflags & DOOR_TOGGLE)
1123 return; // don't come down automatically
1124 self.think = door_rotating_go_down;
1125 self.nextthink = self.ltime + self.wait;
1128 void door_rotating_hit_bottom()
1130 if (self.noise1 != "")
1131 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1132 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1134 self.pos2 = '0 0 0' - self.pos2;
1137 self.state = STATE_BOTTOM;
1140 void door_rotating_go_down()
1142 if (self.noise2 != "")
1143 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1144 if (self.max_health)
1146 self.takedamage = DAMAGE_YES;
1147 self.health = self.max_health;
1150 self.state = STATE_DOWN;
1151 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1154 void door_rotating_go_up()
1156 if (self.state == STATE_UP)
1157 return; // already going up
1159 if (self.state == STATE_TOP)
1160 { // reset top wait time
1161 self.nextthink = self.ltime + self.wait;
1164 if (self.noise2 != "")
1165 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1166 self.state = STATE_UP;
1167 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1170 oldmessage = self.message;
1173 self.message = oldmessage;
1180 =============================================================================
1184 =============================================================================
1188 entity spawn_field(vector fmins, vector fmaxs)
1194 trigger.classname = "doortriggerfield";
1195 trigger.movetype = MOVETYPE_NONE;
1196 trigger.solid = SOLID_TRIGGER;
1197 trigger.owner = self;
1198 trigger.touch = door_trigger_touch;
1202 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1207 entity LinkDoors_nextent(entity cur, entity near, entity pass)
1209 while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
1215 float LinkDoors_isconnected(entity e1, entity e2, entity pass)
1218 if (e1.absmin_x > e2.absmax_x + DELTA)
1220 if (e1.absmin_y > e2.absmax_y + DELTA)
1222 if (e1.absmin_z > e2.absmax_z + DELTA)
1224 if (e2.absmin_x > e1.absmax_x + DELTA)
1226 if (e2.absmin_y > e1.absmax_y + DELTA)
1228 if (e2.absmin_z > e1.absmax_z + DELTA)
1243 vector cmins, cmaxs;
1246 return; // already linked by another door
1247 if (self.spawnflags & 4)
1249 self.owner = self.enemy = self;
1257 self.trigger_field = spawn_field(self.absmin, self.absmax);
1259 return; // don't want to link this door
1262 FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
1264 // set owner, and make a loop of the chain
1265 dprint("LinkDoors: linking doors:");
1266 for(t = self; ; t = t.enemy)
1268 dprint(" ", etos(t));
1270 if(t.enemy == world)
1278 // collect health, targetname, message, size
1279 cmins = self.absmin;
1280 cmaxs = self.absmax;
1281 for(t = self; ; t = t.enemy)
1283 if(t.health && !self.health)
1284 self.health = t.health;
1285 if((t.targetname != "") && (self.targetname == ""))
1286 self.targetname = t.targetname;
1287 if((t.message != "") && (self.message == ""))
1288 self.message = t.message;
1289 if (t.absmin_x < cmins_x)
1290 cmins_x = t.absmin_x;
1291 if (t.absmin_y < cmins_y)
1292 cmins_y = t.absmin_y;
1293 if (t.absmin_z < cmins_z)
1294 cmins_z = t.absmin_z;
1295 if (t.absmax_x > cmaxs_x)
1296 cmaxs_x = t.absmax_x;
1297 if (t.absmax_y > cmaxs_y)
1298 cmaxs_y = t.absmax_y;
1299 if (t.absmax_z > cmaxs_z)
1300 cmaxs_z = t.absmax_z;
1305 // distribute health, targetname, message
1306 for(t = self; t; t = t.enemy)
1308 t.health = self.health;
1309 t.targetname = self.targetname;
1310 t.message = self.message;
1315 // shootable, or triggered doors just needed the owner/enemy links,
1316 // they don't spawn a field
1325 self.trigger_field = spawn_field(cmins, cmaxs);
1329 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1330 if two doors touch, they are assumed to be connected and operate as a unit.
1332 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1334 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).
1336 GOLD_KEY causes the door to open only if the activator holds a gold key.
1338 SILVER_KEY causes the door to open only if the activator holds a silver key.
1340 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1341 "angle" determines the opening direction
1342 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1343 "health" if set, door must be shot open
1344 "speed" movement speed (100 default)
1345 "wait" wait before returning (3 default, -1 = never return)
1346 "lip" lip remaining at end of move (8 default)
1347 "dmg" damage to inflict when blocked (2 default)
1354 FIXME: only one sound set available at the time being
1358 void door_init_startopen()
1360 setorigin (self, self.pos2);
1361 self.pos2 = self.pos1;
1362 self.pos1 = self.origin;
1367 setorigin(self, self.pos1);
1368 self.velocity = '0 0 0';
1369 self.state = STATE_BOTTOM;
1370 self.think = func_null;
1374 // spawnflags require key (for now only func_door)
1375 #define SPAWNFLAGS_GOLD_KEY 8
1376 #define SPAWNFLAGS_SILVER_KEY 16
1377 void spawnfunc_func_door()
1379 // Quake 1 keys compatibility
1380 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1381 self.itemkeys |= ITEM_KEY_BIT(0);
1382 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1383 self.itemkeys |= ITEM_KEY_BIT(1);
1385 //if (!self.deathtype) // map makers can override this
1386 // self.deathtype = " got in the way";
1389 self.max_health = self.health;
1390 if not(InitMovingBrushTrigger())
1392 self.effects |= EF_LOWPRECISION;
1393 self.classname = "door";
1395 self.blocked = door_blocked;
1396 self.use = door_use;
1398 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1399 // if(self.spawnflags & 8)
1400 // self.dmg = 10000;
1402 if(self.dmg && (self.message == ""))
1403 self.message = "was squished";
1404 if(self.dmg && (self.message2 == ""))
1405 self.message2 = "was squished by";
1407 if (self.sounds > 0)
1409 precache_sound ("plats/medplat1.wav");
1410 precache_sound ("plats/medplat2.wav");
1411 self.noise2 = "plats/medplat1.wav";
1412 self.noise1 = "plats/medplat2.wav";
1422 self.pos1 = self.origin;
1423 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1425 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1426 // but spawn in the open position
1427 if (self.spawnflags & DOOR_START_OPEN)
1428 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1430 self.state = STATE_BOTTOM;
1434 self.takedamage = DAMAGE_YES;
1435 self.event_damage = door_damage;
1441 self.touch = door_touch;
1443 // LinkDoors can't be done until all of the doors have been spawned, so
1444 // the sizes can be detected properly.
1445 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1447 self.reset = door_reset;
1450 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1451 if two doors touch, they are assumed to be connected and operate as a unit.
1453 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1455 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1456 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1457 must have set trigger_reverse to 1.
1458 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1460 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).
1462 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1463 "angle" determines the destination angle for opening. negative values reverse the direction.
1464 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1465 "health" if set, door must be shot open
1466 "speed" movement speed (100 default)
1467 "wait" wait before returning (3 default, -1 = never return)
1468 "dmg" damage to inflict when blocked (2 default)
1475 FIXME: only one sound set available at the time being
1478 void door_rotating_reset()
1480 self.angles = self.pos1;
1481 self.avelocity = '0 0 0';
1482 self.state = STATE_BOTTOM;
1483 self.think = func_null;
1487 void door_rotating_init_startopen()
1489 self.angles = self.movedir;
1490 self.pos2 = '0 0 0';
1491 self.pos1 = self.movedir;
1495 void spawnfunc_func_door_rotating()
1498 //if (!self.deathtype) // map makers can override this
1499 // self.deathtype = " got in the way";
1501 // I abuse "movedir" for denoting the axis for now
1502 if (self.spawnflags & 64) // X (untested)
1503 self.movedir = '0 0 1';
1504 else if (self.spawnflags & 128) // Y (untested)
1505 self.movedir = '1 0 0';
1507 self.movedir = '0 1 0';
1509 if (self.angles_y==0) self.angles_y = 90;
1511 self.movedir = self.movedir * self.angles_y;
1512 self.angles = '0 0 0';
1514 self.max_health = self.health;
1515 self.avelocity = self.movedir;
1516 if not(InitMovingBrushTrigger())
1518 self.velocity = '0 0 0';
1519 //self.effects |= EF_LOWPRECISION;
1520 self.classname = "door_rotating";
1522 self.blocked = door_blocked;
1523 self.use = door_use;
1525 if(self.spawnflags & 8)
1528 if(self.dmg && (self.message == ""))
1529 self.message = "was squished";
1530 if(self.dmg && (self.message2 == ""))
1531 self.message2 = "was squished by";
1533 if (self.sounds > 0)
1535 precache_sound ("plats/medplat1.wav");
1536 precache_sound ("plats/medplat2.wav");
1537 self.noise2 = "plats/medplat1.wav";
1538 self.noise1 = "plats/medplat2.wav";
1545 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1547 self.pos1 = '0 0 0';
1548 self.pos2 = self.movedir;
1550 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1551 // but spawn in the open position
1552 if (self.spawnflags & DOOR_START_OPEN)
1553 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1555 self.state = STATE_BOTTOM;
1559 self.takedamage = DAMAGE_YES;
1560 self.event_damage = door_damage;
1566 self.touch = door_touch;
1568 // LinkDoors can't be done until all of the doors have been spawned, so
1569 // the sizes can be detected properly.
1570 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1572 self.reset = door_rotating_reset;
1576 =============================================================================
1580 =============================================================================
1583 void() fd_secret_move1;
1584 void() fd_secret_move2;
1585 void() fd_secret_move3;
1586 void() fd_secret_move4;
1587 void() fd_secret_move5;
1588 void() fd_secret_move6;
1589 void() fd_secret_done;
1591 const float SECRET_OPEN_ONCE = 1; // stays open
1592 const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1593 const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1594 const float SECRET_NO_SHOOT = 8; // only opened by trigger
1595 const float SECRET_YES_SHOOT = 16; // shootable even if targeted
1597 void fd_secret_use()
1600 string message_save;
1602 self.health = 10000;
1603 self.bot_attack = TRUE;
1605 // exit if still moving around...
1606 if (self.origin != self.oldorigin)
1609 message_save = self.message;
1610 self.message = ""; // no more message
1611 SUB_UseTargets(); // fire all targets / killtargets
1612 self.message = message_save;
1614 self.velocity = '0 0 0';
1616 // Make a sound, wait a little...
1618 if (self.noise1 != "")
1619 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1620 self.nextthink = self.ltime + 0.1;
1622 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1623 makevectors(self.mangle);
1627 if (self.spawnflags & SECRET_1ST_DOWN)
1628 self.t_width = fabs(v_up * self.size);
1630 self.t_width = fabs(v_right * self.size);
1634 self.t_length = fabs(v_forward * self.size);
1636 if (self.spawnflags & SECRET_1ST_DOWN)
1637 self.dest1 = self.origin - v_up * self.t_width;
1639 self.dest1 = self.origin + v_right * (self.t_width * temp);
1641 self.dest2 = self.dest1 + v_forward * self.t_length;
1642 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1643 if (self.noise2 != "")
1644 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1647 void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1652 // Wait after first movement...
1653 void fd_secret_move1()
1655 self.nextthink = self.ltime + 1.0;
1656 self.think = fd_secret_move2;
1657 if (self.noise3 != "")
1658 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1661 // Start moving sideways w/sound...
1662 void fd_secret_move2()
1664 if (self.noise2 != "")
1665 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1666 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1669 // Wait here until time to go back...
1670 void fd_secret_move3()
1672 if (self.noise3 != "")
1673 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1674 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1676 self.nextthink = self.ltime + self.wait;
1677 self.think = fd_secret_move4;
1682 void fd_secret_move4()
1684 if (self.noise2 != "")
1685 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1686 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1690 void fd_secret_move5()
1692 self.nextthink = self.ltime + 1.0;
1693 self.think = fd_secret_move6;
1694 if (self.noise3 != "")
1695 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1698 void fd_secret_move6()
1700 if (self.noise2 != "")
1701 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1702 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1705 void fd_secret_done()
1707 if (self.spawnflags&SECRET_YES_SHOOT)
1709 self.health = 10000;
1710 self.takedamage = DAMAGE_YES;
1711 //self.th_pain = fd_secret_use;
1713 if (self.noise3 != "")
1714 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1717 void secret_blocked()
1719 if (time < self.attack_finished_single)
1721 self.attack_finished_single = time + 0.5;
1722 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1734 if not(other.iscreature)
1736 if (self.attack_finished_single > time)
1739 self.attack_finished_single = time + 2;
1743 if (IS_CLIENT(other))
1744 centerprint (other, self.message);
1745 play2(other, "misc/talk.wav");
1751 if (self.spawnflags&SECRET_YES_SHOOT)
1753 self.health = 10000;
1754 self.takedamage = DAMAGE_YES;
1756 setorigin(self, self.oldorigin);
1757 self.think = func_null;
1761 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1762 Basic secret door. Slides back, then to the side. Angle determines direction.
1763 wait = # of seconds before coming back
1764 1st_left = 1st move is left of arrow
1765 1st_down = 1st move is down from arrow
1766 always_shoot = even if targeted, keep shootable
1767 t_width = override WIDTH to move back (or height if going down)
1768 t_length = override LENGTH to move sideways
1769 "dmg" damage to inflict when blocked (2 default)
1771 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1778 void spawnfunc_func_door_secret()
1780 /*if (!self.deathtype) // map makers can override this
1781 self.deathtype = " got in the way";*/
1787 self.mangle = self.angles;
1788 self.angles = '0 0 0';
1789 self.classname = "door";
1790 if not(InitMovingBrushTrigger())
1792 self.effects |= EF_LOWPRECISION;
1794 self.touch = secret_touch;
1795 self.blocked = secret_blocked;
1797 self.use = fd_secret_use;
1802 self.spawnflags |= SECRET_YES_SHOOT;
1804 if(self.spawnflags&SECRET_YES_SHOOT)
1806 self.health = 10000;
1807 self.takedamage = DAMAGE_YES;
1808 self.event_damage = fd_secret_damage;
1810 self.oldorigin = self.origin;
1812 self.wait = 5; // 5 seconds before closing
1814 self.reset = secret_reset;
1818 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1819 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1820 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
1821 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1822 height: amplitude modifier (default 32)
1823 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1824 noise: path/name of looping .wav file to play.
1825 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1829 void func_fourier_controller_think()
1834 self.nextthink = time + 0.1;
1835 if not (self.owner.active == ACTIVE_ACTIVE)
1837 self.owner.velocity = '0 0 0';
1842 n = floor((tokenize_console(self.owner.netname)) / 5);
1843 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1845 v = self.owner.destvec;
1847 for(i = 0; i < n; ++i)
1849 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1850 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;
1853 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1854 // * 10 so it will arrive in 0.1 sec
1855 self.owner.velocity = (v - self.owner.origin) * 10;
1858 void spawnfunc_func_fourier()
1861 if (self.noise != "")
1863 precache_sound(self.noise);
1864 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
1871 self.destvec = self.origin;
1872 self.cnt = 360 / self.speed;
1874 self.blocked = generic_plat_blocked;
1875 if(self.dmg && (self.message == ""))
1876 self.message = " was squished";
1877 if(self.dmg && (self.message2 == ""))
1878 self.message2 = "was squished by";
1879 if(self.dmg && (!self.dmgtime))
1880 self.dmgtime = 0.25;
1881 self.dmgtime2 = time;
1883 if(self.netname == "")
1884 self.netname = "1 0 0 0 1";
1886 if not(InitMovingBrushTrigger())
1889 self.active = ACTIVE_ACTIVE;
1891 // wait for targets to spawn
1892 controller = spawn();
1893 controller.classname = "func_fourier_controller";
1894 controller.owner = self;
1895 controller.nextthink = time + 1;
1896 controller.think = func_fourier_controller_think;
1897 self.nextthink = self.ltime + 999999999;
1898 self.think = SUB_NullThink; // for PushMove
1900 // Savage: Reduce bandwith, critical on e.g. nexdm02
1901 self.effects |= EF_LOWPRECISION;
1903 // TODO make a reset function for this one
1906 // reusing some fields havocbots declared
1907 .entity wp00, wp01, wp02, wp03;
1909 .float targetfactor, target2factor, target3factor, target4factor;
1910 .vector targetnormal, target2normal, target3normal, target4normal;
1912 vector func_vectormamamam_origin(entity o, float t)
1924 p = e.origin + t * e.velocity;
1926 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1928 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1934 p = e.origin + t * e.velocity;
1936 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1938 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1944 p = e.origin + t * e.velocity;
1946 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1948 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1954 p = e.origin + t * e.velocity;
1956 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1958 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1964 void func_vectormamamam_controller_think()
1966 self.nextthink = time + 0.1;
1968 if not (self.owner.active == ACTIVE_ACTIVE)
1970 self.owner.velocity = '0 0 0';
1974 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1975 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1978 void func_vectormamamam_findtarget()
1980 if(self.target != "")
1981 self.wp00 = find(world, targetname, self.target);
1983 if(self.target2 != "")
1984 self.wp01 = find(world, targetname, self.target2);
1986 if(self.target3 != "")
1987 self.wp02 = find(world, targetname, self.target3);
1989 if(self.target4 != "")
1990 self.wp03 = find(world, targetname, self.target4);
1992 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1993 objerror("No reference entity found, so there is nothing to move. Aborting.");
1995 self.destvec = self.origin - func_vectormamamam_origin(self, 0);
1998 controller = spawn();
1999 controller.classname = "func_vectormamamam_controller";
2000 controller.owner = self;
2001 controller.nextthink = time + 1;
2002 controller.think = func_vectormamamam_controller_think;
2005 void spawnfunc_func_vectormamamam()
2007 if (self.noise != "")
2009 precache_sound(self.noise);
2010 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
2013 if(!self.targetfactor)
2014 self.targetfactor = 1;
2016 if(!self.target2factor)
2017 self.target2factor = 1;
2019 if(!self.target3factor)
2020 self.target3factor = 1;
2022 if(!self.target4factor)
2023 self.target4factor = 1;
2025 if(vlen(self.targetnormal))
2026 self.targetnormal = normalize(self.targetnormal);
2028 if(vlen(self.target2normal))
2029 self.target2normal = normalize(self.target2normal);
2031 if(vlen(self.target3normal))
2032 self.target3normal = normalize(self.target3normal);
2034 if(vlen(self.target4normal))
2035 self.target4normal = normalize(self.target4normal);
2037 self.blocked = generic_plat_blocked;
2038 if(self.dmg && (self.message == ""))
2039 self.message = " was squished";
2040 if(self.dmg && (self.message == ""))
2041 self.message2 = "was squished by";
2042 if(self.dmg && (!self.dmgtime))
2043 self.dmgtime = 0.25;
2044 self.dmgtime2 = time;
2046 if(self.netname == "")
2047 self.netname = "1 0 0 0 1";
2049 if not(InitMovingBrushTrigger())
2052 // wait for targets to spawn
2053 self.nextthink = self.ltime + 999999999;
2054 self.think = SUB_NullThink; // for PushMove
2056 // Savage: Reduce bandwith, critical on e.g. nexdm02
2057 self.effects |= EF_LOWPRECISION;
2059 self.active = ACTIVE_ACTIVE;
2061 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2064 void conveyor_think()
2068 // set myself as current conveyor where possible
2069 for(e = world; (e = findentity(e, conveyor, self)); )
2074 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2075 if(!e.conveyor.state)
2078 vector emin = e.absmin;
2079 vector emax = e.absmax;
2080 if(self.solid == SOLID_BSP)
2085 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2086 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2090 for(e = world; (e = findentity(e, conveyor, self)); )
2092 if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
2093 continue; // done in SV_PlayerPhysics
2095 setorigin(e, e.origin + self.movedir * sys_frametime);
2096 move_out_of_solid(e);
2097 UpdateCSQCProjectile(e);
2099 // stupid conveyor code
2100 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2101 if(trace_fraction > 0)
2102 setorigin(e, trace_endpos);
2107 self.nextthink = time;
2112 self.state = !self.state;
2115 void conveyor_reset()
2117 self.state = (self.spawnflags & 1);
2120 void conveyor_init()
2124 self.movedir = self.movedir * self.speed;
2125 self.think = conveyor_think;
2126 self.nextthink = time;
2129 self.use = conveyor_use;
2130 self.reset = conveyor_reset;
2137 void spawnfunc_trigger_conveyor()
2144 void spawnfunc_func_conveyor()
2147 InitMovingBrushTrigger();
2148 self.movetype = MOVETYPE_NONE;