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);
284 self.target = targ.target;
286 objerror("train_next: no next target");
287 self.wait = targ.wait;
292 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
294 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
297 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
300 void func_train_find()
303 targ = find(world, targetname, self.target);
304 self.target = targ.target;
306 objerror("func_train_find: no next target");
307 setorigin(self, targ.origin - self.mins);
308 self.nextthink = self.ltime + 1;
309 self.think = train_next;
312 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
313 Ridable platform, targets spawnfunc_path_corner path to follow.
314 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
315 target : targetname of first spawnfunc_path_corner (starts here)
317 void spawnfunc_func_train()
319 if (self.noise != "")
320 precache_sound(self.noise);
323 objerror("func_train without a target");
327 if not(InitMovingBrushTrigger())
329 self.effects |= EF_LOWPRECISION;
331 // wait for targets to spawn
332 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
334 self.blocked = generic_plat_blocked;
335 if(self.dmg & (!self.message))
336 self.message = " was squished";
337 if(self.dmg && (!self.message2))
338 self.message2 = "was squished by";
339 if(self.dmg && (!self.dmgtime))
341 self.dmgtime2 = time;
343 // TODO make a reset function for this one
346 void func_rotating_setactive(float astate)
349 if (astate == ACTIVE_TOGGLE)
351 if(self.active == ACTIVE_ACTIVE)
352 self.active = ACTIVE_NOT;
354 self.active = ACTIVE_ACTIVE;
357 self.active = astate;
359 if(self.active == ACTIVE_NOT)
360 self.avelocity = '0 0 0';
362 self.avelocity = self.pos1;
365 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
366 Brush model that spins in place on one axis (default Z).
367 speed : speed to rotate (in degrees per second)
368 noise : path/name of looping .wav file to play.
369 dmg : Do this mutch dmg every .dmgtime intervall when blocked
373 void spawnfunc_func_rotating()
375 if (self.noise != "")
377 precache_sound(self.noise);
378 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
381 self.active = ACTIVE_ACTIVE;
382 self.setactive = func_rotating_setactive;
386 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
387 if (self.spawnflags & 4) // X (untested)
388 self.avelocity = '0 0 1' * self.speed;
389 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
390 else if (self.spawnflags & 8) // Y (untested)
391 self.avelocity = '1 0 0' * self.speed;
392 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
394 self.avelocity = '0 1 0' * self.speed;
396 self.pos1 = self.avelocity;
398 if(self.dmg & (!self.message))
399 self.message = " was squished";
400 if(self.dmg && (!self.message2))
401 self.message2 = "was squished by";
404 if(self.dmg && (!self.dmgtime))
407 self.dmgtime2 = time;
409 if not(InitMovingBrushTrigger())
411 // no EF_LOWPRECISION here, as rounding angles is bad
413 self.blocked = generic_plat_blocked;
415 // wait for targets to spawn
416 self.nextthink = self.ltime + 999999999;
417 self.think = SUB_Null;
419 // TODO make a reset function for this one
423 void func_bobbing_controller_think()
426 self.nextthink = time + 0.1;
428 if not (self.owner.active == ACTIVE_ACTIVE)
430 self.owner.velocity = '0 0 0';
434 // calculate sinewave using makevectors
435 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
436 v = self.owner.destvec + self.owner.movedir * v_forward_y;
437 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
438 // * 10 so it will arrive in 0.1 sec
439 self.owner.velocity = (v - self.owner.origin) * 10;
442 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
443 Brush model that moves back and forth on one axis (default Z).
444 speed : how long one cycle takes in seconds (default 4)
445 height : how far the cycle moves (default 32)
446 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
447 noise : path/name of looping .wav file to play.
448 dmg : Do this mutch dmg every .dmgtime intervall when blocked
451 void spawnfunc_func_bobbing()
454 if (self.noise != "")
456 precache_sound(self.noise);
457 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
463 // center of bobbing motion
464 self.destvec = self.origin;
465 // time scale to get degrees
466 self.cnt = 360 / self.speed;
468 self.active = ACTIVE_ACTIVE;
470 // damage when blocked
471 self.blocked = generic_plat_blocked;
472 if(self.dmg & (!self.message))
473 self.message = " was squished";
474 if(self.dmg && (!self.message2))
475 self.message2 = "was squished by";
476 if(self.dmg && (!self.dmgtime))
478 self.dmgtime2 = time;
481 if (self.spawnflags & 1) // X
482 self.movedir = '1 0 0' * self.height;
483 else if (self.spawnflags & 2) // Y
484 self.movedir = '0 1 0' * self.height;
486 self.movedir = '0 0 1' * self.height;
488 if not(InitMovingBrushTrigger())
491 // wait for targets to spawn
492 controller = spawn();
493 controller.classname = "func_bobbing_controller";
494 controller.owner = self;
495 controller.nextthink = time + 1;
496 controller.think = func_bobbing_controller_think;
497 self.nextthink = self.ltime + 999999999;
498 self.think = SUB_Null;
500 // Savage: Reduce bandwith, critical on e.g. nexdm02
501 self.effects |= EF_LOWPRECISION;
503 // TODO make a reset function for this one
507 void func_pendulum_controller_think()
510 self.nextthink = time + 0.1;
512 if not (self.owner.active == ACTIVE_ACTIVE)
514 self.owner.avelocity_x = 0;
518 // calculate sinewave using makevectors
519 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
520 v = self.owner.speed * v_forward_y + self.cnt;
521 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
523 // * 10 so it will arrive in 0.1 sec
524 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
528 void spawnfunc_func_pendulum()
531 if (self.noise != "")
533 precache_sound(self.noise);
534 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
537 self.active = ACTIVE_ACTIVE;
539 // keys: angle, speed, phase, noise, freq
543 // not initializing self.dmg to 2, to allow damageless pendulum
545 if(self.dmg & (!self.message))
546 self.message = " was squished";
547 if(self.dmg && (!self.message2))
548 self.message2 = "was squished by";
549 if(self.dmg && (!self.dmgtime))
551 self.dmgtime2 = time;
553 self.blocked = generic_plat_blocked;
555 self.avelocity_z = 0.0000001;
556 if not(InitMovingBrushTrigger())
561 // find pendulum length (same formula as Q3A)
562 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
565 // copy initial angle
566 self.cnt = self.angles_z;
568 // wait for targets to spawn
569 controller = spawn();
570 controller.classname = "func_pendulum_controller";
571 controller.owner = self;
572 controller.nextthink = time + 1;
573 controller.think = func_pendulum_controller_think;
574 self.nextthink = self.ltime + 999999999;
575 self.think = SUB_Null;
577 //self.effects |= EF_LOWPRECISION;
579 // TODO make a reset function for this one
582 // button and multiple button
585 void() button_return;
589 self.state = STATE_TOP;
590 self.nextthink = self.ltime + self.wait;
591 self.think = button_return;
592 activator = self.enemy;
594 self.frame = 1; // use alternate textures
599 self.state = STATE_BOTTOM;
604 self.state = STATE_DOWN;
605 SUB_CalcMove (self.pos1, self.speed, button_done);
606 self.frame = 0; // use normal textures
608 self.takedamage = DAMAGE_YES; // can be shot again
612 void button_blocked()
614 // do nothing, just don't come all the way back out
620 self.health = self.max_health;
621 self.takedamage = DAMAGE_NO; // will be reset upon return
623 if (self.state == STATE_UP || self.state == STATE_TOP)
626 if (self.noise != "")
627 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
629 self.state = STATE_UP;
630 SUB_CalcMove (self.pos2, self.speed, button_wait);
635 self.health = self.max_health;
636 setorigin(self, self.pos1);
637 self.frame = 0; // use normal textures
638 self.state = STATE_BOTTOM;
640 self.takedamage = DAMAGE_YES; // can be shot again
645 // if (activator.classname != "player")
647 // dprint(activator.classname);
648 // dprint(" triggered a button\n");
651 if not (self.active == ACTIVE_ACTIVE)
654 self.enemy = activator;
660 // if (activator.classname != "player")
662 // dprint(activator.classname);
663 // dprint(" touched a button\n");
667 if not(other.iscreature)
669 if(other.velocity * self.movedir < 0)
673 self.enemy = other.owner;
677 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
679 if(self.spawnflags & DOOR_NOSPLASH)
680 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
682 self.health = self.health - damage;
683 if (self.health <= 0)
685 // if (activator.classname != "player")
687 // dprint(activator.classname);
688 // dprint(" killed a button\n");
690 self.enemy = damage_attacker;
696 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
697 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.
699 "angle" determines the opening direction
700 "target" all entities with a matching targetname will be used
701 "speed" override the default 40 speed
702 "wait" override the default 1 second wait (-1 = never return)
703 "lip" override the default 4 pixel lip remaining at end of move
704 "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
711 void spawnfunc_func_button()
715 if not(InitMovingBrushTrigger())
717 self.effects |= EF_LOWPRECISION;
719 self.blocked = button_blocked;
720 self.use = button_use;
722 // if (self.health == 0) // all buttons are now shootable
726 self.max_health = self.health;
727 self.event_damage = button_damage;
728 self.takedamage = DAMAGE_YES;
731 self.touch = button_touch;
741 precache_sound(self.noise);
743 self.active = ACTIVE_ACTIVE;
745 self.pos1 = self.origin;
746 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
747 self.flags |= FL_NOTARGET;
753 float DOOR_START_OPEN = 1;
754 float DOOR_DONT_LINK = 4;
755 float DOOR_TOGGLE = 32;
759 Doors are similar to buttons, but can spawn a fat trigger field around them
760 to open without a touch, and they link together to form simultanious
763 Door.owner is the master door. If there is only one door, it points to itself.
764 If multiple doors, all will point to a single one.
766 Door.enemy chains from the master door through all doors linked in the chain.
771 =============================================================================
775 =============================================================================
780 void() door_rotating_go_down;
781 void() door_rotating_go_up;
786 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
787 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
790 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
791 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
793 //Dont chamge direction for dead or dying stuff
794 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
797 if (self.state == STATE_DOWN)
798 if (self.classname == "door")
803 door_rotating_go_up ();
806 if (self.classname == "door")
811 door_rotating_go_down ();
815 //gib dying stuff just to make sure
816 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
817 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
821 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
822 // if a door has a negative wait, it would never come back if blocked,
823 // so let it just squash the object to death real fast
824 /* if (self.wait >= 0)
826 if (self.state == STATE_DOWN)
837 if (self.noise1 != "")
838 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
839 self.state = STATE_TOP;
840 if (self.spawnflags & DOOR_TOGGLE)
841 return; // don't come down automatically
842 if (self.classname == "door")
844 self.think = door_go_down;
847 self.think = door_rotating_go_down;
849 self.nextthink = self.ltime + self.wait;
852 void door_hit_bottom()
854 if (self.noise1 != "")
855 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
856 self.state = STATE_BOTTOM;
861 if (self.noise2 != "")
862 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
865 self.takedamage = DAMAGE_YES;
866 self.health = self.max_health;
869 self.state = STATE_DOWN;
870 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
875 if (self.state == STATE_UP)
876 return; // already going up
878 if (self.state == STATE_TOP)
879 { // reset top wait time
880 self.nextthink = self.ltime + self.wait;
884 if (self.noise2 != "")
885 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
886 self.state = STATE_UP;
887 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
890 oldmessage = self.message;
893 self.message = oldmessage;
899 =============================================================================
903 =============================================================================
906 float door_check_keys(void) {
916 if not(door.itemkeys)
919 // this door require a key
920 // only a player can have a key
921 if (other.classname != "player")
924 if (item_keys_usekey(door, other)) {
925 // some keys were used
926 if (other.key_door_messagetime <= time) {
927 play2(other, "misc/talk.wav");
928 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
929 other.key_door_messagetime = time + 2;
933 if (other.key_door_messagetime <= time) {
934 play2(other, "misc/talk.wav");
935 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
936 other.key_door_messagetime = time + 2;
941 // door is now unlocked
942 play2(other, "misc/talk.wav");
943 centerprint(other, "Door unlocked!");
955 if (self.owner != self)
956 objerror ("door_fire: self.owner != self");
960 if (self.spawnflags & DOOR_TOGGLE)
962 if (self.state == STATE_UP || self.state == STATE_TOP)
967 if (self.classname == "door")
973 door_rotating_go_down ();
976 } while ( (self != starte) && (self != world) );
982 // trigger all paired doors
986 if (self.classname == "door")
991 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
992 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
994 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
995 self.pos2 = '0 0 0' - self.pos2;
997 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
998 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
999 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1001 door_rotating_go_up ();
1005 } while ( (self != starte) && (self != world) );
1014 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1026 void door_trigger_touch()
1028 if (other.health < 1)
1029 if not(other.iscreature && other.deadflag == DEAD_NO)
1032 if (time < self.attack_finished_single)
1035 // check if door is locked
1036 if (!door_check_keys())
1039 self.attack_finished_single = time + 1;
1048 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1051 if(self.spawnflags & DOOR_NOSPLASH)
1052 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1054 self.health = self.health - damage;
1056 if (self.itemkeys) {
1057 // don't allow opening doors through damage if keys are required
1061 if (self.health <= 0)
1065 self.health = self.max_health;
1066 self.takedamage = DAMAGE_NO; // wil be reset upon return
1082 if(other.classname != "player")
1084 if (self.owner.attack_finished_single > time)
1087 self.owner.attack_finished_single = time + 2;
1089 if (!(self.owner.dmg) && (self.owner.message != ""))
1091 if (other.flags & FL_CLIENT)
1092 centerprint (other, self.owner.message);
1093 play2(other, "misc/talk.wav");
1098 void door_generic_plat_blocked()
1101 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1102 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1105 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1106 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1108 //Dont chamge direction for dead or dying stuff
1109 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1112 if (self.state == STATE_DOWN)
1113 door_rotating_go_up ();
1115 door_rotating_go_down ();
1118 //gib dying stuff just to make sure
1119 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1120 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1124 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1125 // if a door has a negative wait, it would never come back if blocked,
1126 // so let it just squash the object to death real fast
1127 /* if (self.wait >= 0)
1129 if (self.state == STATE_DOWN)
1130 door_rotating_go_up ();
1132 door_rotating_go_down ();
1138 void door_rotating_hit_top()
1140 if (self.noise1 != "")
1141 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1142 self.state = STATE_TOP;
1143 if (self.spawnflags & DOOR_TOGGLE)
1144 return; // don't come down automatically
1145 self.think = door_rotating_go_down;
1146 self.nextthink = self.ltime + self.wait;
1149 void door_rotating_hit_bottom()
1151 if (self.noise1 != "")
1152 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1153 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1155 self.pos2 = '0 0 0' - self.pos2;
1158 self.state = STATE_BOTTOM;
1161 void door_rotating_go_down()
1163 if (self.noise2 != "")
1164 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1165 if (self.max_health)
1167 self.takedamage = DAMAGE_YES;
1168 self.health = self.max_health;
1171 self.state = STATE_DOWN;
1172 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1175 void door_rotating_go_up()
1177 if (self.state == STATE_UP)
1178 return; // already going up
1180 if (self.state == STATE_TOP)
1181 { // reset top wait time
1182 self.nextthink = self.ltime + self.wait;
1185 if (self.noise2 != "")
1186 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1187 self.state = STATE_UP;
1188 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1191 oldmessage = self.message;
1194 self.message = oldmessage;
1201 =============================================================================
1205 =============================================================================
1209 entity spawn_field(vector fmins, vector fmaxs)
1215 trigger.classname = "doortriggerfield";
1216 trigger.movetype = MOVETYPE_NONE;
1217 trigger.solid = SOLID_TRIGGER;
1218 trigger.owner = self;
1219 trigger.touch = door_trigger_touch;
1223 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1228 entity LinkDoors_nextent(entity cur, entity near, entity pass)
1230 while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
1236 float LinkDoors_isconnected(entity e1, entity e2, entity pass)
1239 if (e1.absmin_x > e2.absmax_x + DELTA)
1241 if (e1.absmin_y > e2.absmax_y + DELTA)
1243 if (e1.absmin_z > e2.absmax_z + DELTA)
1245 if (e2.absmin_x > e1.absmax_x + DELTA)
1247 if (e2.absmin_y > e1.absmax_y + DELTA)
1249 if (e2.absmin_z > e1.absmax_z + DELTA)
1264 vector cmins, cmaxs;
1267 return; // already linked by another door
1268 if (self.spawnflags & 4)
1270 self.owner = self.enemy = self;
1278 self.trigger_field = spawn_field(self.absmin, self.absmax);
1280 return; // don't want to link this door
1283 FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
1285 // set owner, and make a loop of the chain
1286 dprint("LinkDoors: linking doors:");
1287 for(t = self; ; t = t.enemy)
1289 dprint(" ", etos(t));
1291 if(t.enemy == world)
1299 // collect health, targetname, message, size
1300 cmins = self.absmin;
1301 cmaxs = self.absmax;
1302 for(t = self; ; t = t.enemy)
1304 if(t.health && !self.health)
1305 self.health = t.health;
1306 if(t.targetname && !self.targetname)
1307 self.targetname = t.targetname;
1308 if(t.message != "" && self.message == "")
1309 self.message = t.message;
1310 if (t.absmin_x < cmins_x)
1311 cmins_x = t.absmin_x;
1312 if (t.absmin_y < cmins_y)
1313 cmins_y = t.absmin_y;
1314 if (t.absmin_z < cmins_z)
1315 cmins_z = t.absmin_z;
1316 if (t.absmax_x > cmaxs_x)
1317 cmaxs_x = t.absmax_x;
1318 if (t.absmax_y > cmaxs_y)
1319 cmaxs_y = t.absmax_y;
1320 if (t.absmax_z > cmaxs_z)
1321 cmaxs_z = t.absmax_z;
1326 // distribute health, targetname, message
1327 for(t = self; t; t = t.enemy)
1329 t.health = self.health;
1330 t.targetname = self.targetname;
1331 t.message = self.message;
1336 // shootable, or triggered doors just needed the owner/enemy links,
1337 // they don't spawn a field
1346 self.trigger_field = spawn_field(cmins, cmaxs);
1350 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1351 if two doors touch, they are assumed to be connected and operate as a unit.
1353 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1355 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).
1357 GOLD_KEY causes the door to open only if the activator holds a gold key.
1359 SILVER_KEY causes the door to open only if the activator holds a silver key.
1361 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1362 "angle" determines the opening direction
1363 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1364 "health" if set, door must be shot open
1365 "speed" movement speed (100 default)
1366 "wait" wait before returning (3 default, -1 = never return)
1367 "lip" lip remaining at end of move (8 default)
1368 "dmg" damage to inflict when blocked (2 default)
1375 FIXME: only one sound set available at the time being
1379 void door_init_startopen()
1381 setorigin (self, self.pos2);
1382 self.pos2 = self.pos1;
1383 self.pos1 = self.origin;
1388 setorigin(self, self.pos1);
1389 self.velocity = '0 0 0';
1390 self.state = STATE_BOTTOM;
1391 self.think = SUB_Null;
1394 // spawnflags require key (for now only func_door)
1395 #define SPAWNFLAGS_GOLD_KEY 8
1396 #define SPAWNFLAGS_SILVER_KEY 16
1397 void spawnfunc_func_door()
1399 // Quake 1 keys compatibility
1400 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1401 self.itemkeys |= ITEM_KEY_BIT(0);
1402 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1403 self.itemkeys |= ITEM_KEY_BIT(1);
1405 //if (!self.deathtype) // map makers can override this
1406 // self.deathtype = " got in the way";
1409 self.max_health = self.health;
1410 if not(InitMovingBrushTrigger())
1412 self.effects |= EF_LOWPRECISION;
1413 self.classname = "door";
1415 self.blocked = door_blocked;
1416 self.use = door_use;
1418 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1419 // if(self.spawnflags & 8)
1420 // self.dmg = 10000;
1422 if(self.dmg && (!self.message))
1423 self.message = "was squished";
1424 if(self.dmg && (!self.message2))
1425 self.message2 = "was squished by";
1427 if (self.sounds > 0)
1429 precache_sound ("plats/medplat1.wav");
1430 precache_sound ("plats/medplat2.wav");
1431 self.noise2 = "plats/medplat1.wav";
1432 self.noise1 = "plats/medplat2.wav";
1442 self.pos1 = self.origin;
1443 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1445 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1446 // but spawn in the open position
1447 if (self.spawnflags & DOOR_START_OPEN)
1448 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1450 self.state = STATE_BOTTOM;
1454 self.takedamage = DAMAGE_YES;
1455 self.event_damage = door_damage;
1461 self.touch = door_touch;
1463 // LinkDoors can't be done until all of the doors have been spawned, so
1464 // the sizes can be detected properly.
1465 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1467 self.reset = door_reset;
1470 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1471 if two doors touch, they are assumed to be connected and operate as a unit.
1473 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1475 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1476 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1477 must have set trigger_reverse to 1.
1478 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1480 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).
1482 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1483 "angle" determines the destination angle for opening. negative values reverse the direction.
1484 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1485 "health" if set, door must be shot open
1486 "speed" movement speed (100 default)
1487 "wait" wait before returning (3 default, -1 = never return)
1488 "dmg" damage to inflict when blocked (2 default)
1495 FIXME: only one sound set available at the time being
1498 void door_rotating_reset()
1500 self.angles = self.pos1;
1501 self.avelocity = '0 0 0';
1502 self.state = STATE_BOTTOM;
1503 self.think = SUB_Null;
1506 void door_rotating_init_startopen()
1508 self.angles = self.movedir;
1509 self.pos2 = '0 0 0';
1510 self.pos1 = self.movedir;
1514 void spawnfunc_func_door_rotating()
1517 //if (!self.deathtype) // map makers can override this
1518 // self.deathtype = " got in the way";
1520 // I abuse "movedir" for denoting the axis for now
1521 if (self.spawnflags & 64) // X (untested)
1522 self.movedir = '0 0 1';
1523 else if (self.spawnflags & 128) // Y (untested)
1524 self.movedir = '1 0 0';
1526 self.movedir = '0 1 0';
1528 if (self.angles_y==0) self.angles_y = 90;
1530 self.movedir = self.movedir * self.angles_y;
1531 self.angles = '0 0 0';
1533 self.max_health = self.health;
1534 self.avelocity = self.movedir;
1535 if not(InitMovingBrushTrigger())
1537 self.velocity = '0 0 0';
1538 //self.effects |= EF_LOWPRECISION;
1539 self.classname = "door_rotating";
1541 self.blocked = door_blocked;
1542 self.use = door_use;
1544 if(self.spawnflags & 8)
1547 if(self.dmg && (!self.message))
1548 self.message = "was squished";
1549 if(self.dmg && (!self.message2))
1550 self.message2 = "was squished by";
1552 if (self.sounds > 0)
1554 precache_sound ("plats/medplat1.wav");
1555 precache_sound ("plats/medplat2.wav");
1556 self.noise2 = "plats/medplat1.wav";
1557 self.noise1 = "plats/medplat2.wav";
1564 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1566 self.pos1 = '0 0 0';
1567 self.pos2 = self.movedir;
1569 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1570 // but spawn in the open position
1571 if (self.spawnflags & DOOR_START_OPEN)
1572 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1574 self.state = STATE_BOTTOM;
1578 self.takedamage = DAMAGE_YES;
1579 self.event_damage = door_damage;
1585 self.touch = door_touch;
1587 // LinkDoors can't be done until all of the doors have been spawned, so
1588 // the sizes can be detected properly.
1589 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1591 self.reset = door_rotating_reset;
1595 =============================================================================
1599 =============================================================================
1602 void() fd_secret_move1;
1603 void() fd_secret_move2;
1604 void() fd_secret_move3;
1605 void() fd_secret_move4;
1606 void() fd_secret_move5;
1607 void() fd_secret_move6;
1608 void() fd_secret_done;
1610 float SECRET_OPEN_ONCE = 1; // stays open
1611 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1612 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1613 float SECRET_NO_SHOOT = 8; // only opened by trigger
1614 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1617 void fd_secret_use()
1620 string message_save;
1622 self.health = 10000;
1623 self.bot_attack = TRUE;
1625 // exit if still moving around...
1626 if (self.origin != self.oldorigin)
1629 message_save = self.message;
1630 self.message = ""; // no more message
1631 SUB_UseTargets(); // fire all targets / killtargets
1632 self.message = message_save;
1634 self.velocity = '0 0 0';
1636 // Make a sound, wait a little...
1638 if (self.noise1 != "")
1639 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1640 self.nextthink = self.ltime + 0.1;
1642 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1643 makevectors(self.mangle);
1647 if (self.spawnflags & SECRET_1ST_DOWN)
1648 self.t_width = fabs(v_up * self.size);
1650 self.t_width = fabs(v_right * self.size);
1654 self.t_length = fabs(v_forward * self.size);
1656 if (self.spawnflags & SECRET_1ST_DOWN)
1657 self.dest1 = self.origin - v_up * self.t_width;
1659 self.dest1 = self.origin + v_right * (self.t_width * temp);
1661 self.dest2 = self.dest1 + v_forward * self.t_length;
1662 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1663 if (self.noise2 != "")
1664 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1667 // Wait after first movement...
1668 void fd_secret_move1()
1670 self.nextthink = self.ltime + 1.0;
1671 self.think = fd_secret_move2;
1672 if (self.noise3 != "")
1673 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1676 // Start moving sideways w/sound...
1677 void fd_secret_move2()
1679 if (self.noise2 != "")
1680 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1681 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1684 // Wait here until time to go back...
1685 void fd_secret_move3()
1687 if (self.noise3 != "")
1688 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1689 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1691 self.nextthink = self.ltime + self.wait;
1692 self.think = fd_secret_move4;
1697 void fd_secret_move4()
1699 if (self.noise2 != "")
1700 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1701 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1705 void fd_secret_move5()
1707 self.nextthink = self.ltime + 1.0;
1708 self.think = fd_secret_move6;
1709 if (self.noise3 != "")
1710 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1713 void fd_secret_move6()
1715 if (self.noise2 != "")
1716 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1717 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1720 void fd_secret_done()
1722 if (self.spawnflags&SECRET_YES_SHOOT)
1724 self.health = 10000;
1725 self.takedamage = DAMAGE_YES;
1726 //self.th_pain = fd_secret_use;
1728 if (self.noise3 != "")
1729 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1732 void secret_blocked()
1734 if (time < self.attack_finished_single)
1736 self.attack_finished_single = time + 0.5;
1737 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1749 if not(other.iscreature)
1751 if (self.attack_finished_single > time)
1754 self.attack_finished_single = time + 2;
1758 if (other.flags & FL_CLIENT)
1759 centerprint (other, self.message);
1760 play2(other, "misc/talk.wav");
1766 if (self.spawnflags&SECRET_YES_SHOOT)
1768 self.health = 10000;
1769 self.takedamage = DAMAGE_YES;
1771 setorigin(self, self.oldorigin);
1772 self.think = SUB_Null;
1775 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1776 Basic secret door. Slides back, then to the side. Angle determines direction.
1777 wait = # of seconds before coming back
1778 1st_left = 1st move is left of arrow
1779 1st_down = 1st move is down from arrow
1780 always_shoot = even if targeted, keep shootable
1781 t_width = override WIDTH to move back (or height if going down)
1782 t_length = override LENGTH to move sideways
1783 "dmg" damage to inflict when blocked (2 default)
1785 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1792 void spawnfunc_func_door_secret()
1794 /*if (!self.deathtype) // map makers can override this
1795 self.deathtype = " got in the way";*/
1801 self.mangle = self.angles;
1802 self.angles = '0 0 0';
1803 self.classname = "door";
1804 if not(InitMovingBrushTrigger())
1806 self.effects |= EF_LOWPRECISION;
1808 self.touch = secret_touch;
1809 self.blocked = secret_blocked;
1811 self.use = fd_secret_use;
1816 self.spawnflags |= SECRET_YES_SHOOT;
1818 if(self.spawnflags&SECRET_YES_SHOOT)
1820 self.health = 10000;
1821 self.takedamage = DAMAGE_YES;
1822 self.event_damage = fd_secret_use;
1824 self.oldorigin = self.origin;
1826 self.wait = 5; // 5 seconds before closing
1828 self.reset = secret_reset;
1832 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1833 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1834 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
1835 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1836 height: amplitude modifier (default 32)
1837 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1838 noise: path/name of looping .wav file to play.
1839 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1843 void func_fourier_controller_think()
1848 self.nextthink = time + 0.1;
1849 if not (self.owner.active == ACTIVE_ACTIVE)
1851 self.owner.velocity = '0 0 0';
1856 n = floor((tokenize_console(self.owner.netname)) / 5);
1857 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1859 v = self.owner.destvec;
1861 for(i = 0; i < n; ++i)
1863 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1864 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;
1867 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1868 // * 10 so it will arrive in 0.1 sec
1869 self.owner.velocity = (v - self.owner.origin) * 10;
1872 void spawnfunc_func_fourier()
1875 if (self.noise != "")
1877 precache_sound(self.noise);
1878 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1885 self.destvec = self.origin;
1886 self.cnt = 360 / self.speed;
1888 self.blocked = generic_plat_blocked;
1889 if(self.dmg & (!self.message))
1890 self.message = " was squished";
1891 if(self.dmg && (!self.message2))
1892 self.message2 = "was squished by";
1893 if(self.dmg && (!self.dmgtime))
1894 self.dmgtime = 0.25;
1895 self.dmgtime2 = time;
1897 if(self.netname == "")
1898 self.netname = "1 0 0 0 1";
1900 if not(InitMovingBrushTrigger())
1903 self.active = ACTIVE_ACTIVE;
1905 // wait for targets to spawn
1906 controller = spawn();
1907 controller.classname = "func_fourier_controller";
1908 controller.owner = self;
1909 controller.nextthink = time + 1;
1910 controller.think = func_fourier_controller_think;
1911 self.nextthink = self.ltime + 999999999;
1912 self.think = SUB_Null;
1914 // Savage: Reduce bandwith, critical on e.g. nexdm02
1915 self.effects |= EF_LOWPRECISION;
1917 // TODO make a reset function for this one
1920 // reusing some fields havocbots declared
1921 .entity wp00, wp01, wp02, wp03;
1923 .float targetfactor, target2factor, target3factor, target4factor;
1924 .vector targetnormal, target2normal, target3normal, target4normal;
1926 vector func_vectormamamam_origin(entity o, float t)
1938 p = e.origin + t * e.velocity;
1940 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1942 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1948 p = e.origin + t * e.velocity;
1950 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1952 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1958 p = e.origin + t * e.velocity;
1960 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1962 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1968 p = e.origin + t * e.velocity;
1970 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1972 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1978 void func_vectormamamam_controller_think()
1980 self.nextthink = time + 0.1;
1982 if not (self.owner.active == ACTIVE_ACTIVE)
1984 self.owner.velocity = '0 0 0';
1988 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1989 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1992 void func_vectormamamam_findtarget()
1994 if(self.target != "")
1995 self.wp00 = find(world, targetname, self.target);
1997 if(self.target2 != "")
1998 self.wp01 = find(world, targetname, self.target2);
2000 if(self.target3 != "")
2001 self.wp02 = find(world, targetname, self.target3);
2003 if(self.target4 != "")
2004 self.wp03 = find(world, targetname, self.target4);
2006 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2007 objerror("No reference entity found, so there is nothing to move. Aborting.");
2009 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2012 controller = spawn();
2013 controller.classname = "func_vectormamamam_controller";
2014 controller.owner = self;
2015 controller.nextthink = time + 1;
2016 controller.think = func_vectormamamam_controller_think;
2019 void spawnfunc_func_vectormamamam()
2021 if (self.noise != "")
2023 precache_sound(self.noise);
2024 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2027 if(!self.targetfactor)
2028 self.targetfactor = 1;
2030 if(!self.target2factor)
2031 self.target2factor = 1;
2033 if(!self.target3factor)
2034 self.target3factor = 1;
2036 if(!self.target4factor)
2037 self.target4factor = 1;
2039 if(vlen(self.targetnormal))
2040 self.targetnormal = normalize(self.targetnormal);
2042 if(vlen(self.target2normal))
2043 self.target2normal = normalize(self.target2normal);
2045 if(vlen(self.target3normal))
2046 self.target3normal = normalize(self.target3normal);
2048 if(vlen(self.target4normal))
2049 self.target4normal = normalize(self.target4normal);
2051 self.blocked = generic_plat_blocked;
2052 if(self.dmg & (!self.message))
2053 self.message = " was squished";
2054 if(self.dmg && (!self.message2))
2055 self.message2 = "was squished by";
2056 if(self.dmg && (!self.dmgtime))
2057 self.dmgtime = 0.25;
2058 self.dmgtime2 = time;
2060 if(self.netname == "")
2061 self.netname = "1 0 0 0 1";
2063 if not(InitMovingBrushTrigger())
2066 // wait for targets to spawn
2067 self.nextthink = self.ltime + 999999999;
2068 self.think = SUB_Null;
2070 // Savage: Reduce bandwith, critical on e.g. nexdm02
2071 self.effects |= EF_LOWPRECISION;
2073 self.active = ACTIVE_ACTIVE;
2075 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2078 void conveyor_think()
2082 // set myself as current conveyor where possible
2083 for(e = world; (e = findentity(e, conveyor, self)); )
2088 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2089 if(!e.conveyor.state)
2092 vector emin = e.absmin;
2093 vector emax = e.absmax;
2094 if(self.solid == SOLID_BSP)
2099 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2100 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2104 for(e = world; (e = findentity(e, conveyor, self)); )
2106 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2107 continue; // done in SV_PlayerPhysics
2109 setorigin(e, e.origin + self.movedir * sys_frametime);
2110 move_out_of_solid(e);
2111 UpdateCSQCProjectile(e);
2113 // stupid conveyor code
2114 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2115 if(trace_fraction > 0)
2116 setorigin(e, trace_endpos);
2121 self.nextthink = time;
2126 self.state = !self.state;
2129 void conveyor_reset()
2131 self.state = (self.spawnflags & 1);
2134 void conveyor_init()
2138 self.movedir = self.movedir * self.speed;
2139 self.think = conveyor_think;
2140 self.nextthink = time;
2143 self.use = conveyor_use;
2144 self.reset = conveyor_reset;
2151 void spawnfunc_trigger_conveyor()
2158 void spawnfunc_func_conveyor()
2161 InitMovingBrushTrigger();
2162 self.movetype = MOVETYPE_NONE;