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 float EntitiesTouching(entity e1, entity e2)
1230 if (e1.absmin_x > e2.absmax_x)
1232 if (e1.absmin_y > e2.absmax_y)
1234 if (e1.absmin_z > e2.absmax_z)
1236 if (e1.absmax_x < e2.absmin_x)
1238 if (e1.absmax_y < e2.absmin_y)
1240 if (e1.absmax_z < e2.absmin_z)
1256 vector cmins, cmaxs;
1259 return; // already linked by another door
1260 if (self.spawnflags & 4)
1262 self.owner = self.enemy = self;
1270 self.trigger_field = spawn_field(self.absmin, self.absmax);
1272 return; // don't want to link this door
1275 cmins = self.absmin;
1276 cmaxs = self.absmax;
1283 self.owner = starte; // master door
1286 starte.health = self.health;
1288 starte.targetname = self.targetname;
1289 if (self.message != "")
1290 starte.message = self.message;
1292 t = find(t, classname, self.classname);
1295 self.enemy = starte; // make the chain a loop
1297 // shootable, or triggered doors just needed the owner/enemy links,
1298 // they don't spawn a field
1309 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1314 if (EntitiesTouching(self,t))
1317 objerror ("cross connected doors");
1322 if (t.absmin_x < cmins_x)
1323 cmins_x = t.absmin_x;
1324 if (t.absmin_y < cmins_y)
1325 cmins_y = t.absmin_y;
1326 if (t.absmin_z < cmins_z)
1327 cmins_z = t.absmin_z;
1328 if (t.absmax_x > cmaxs_x)
1329 cmaxs_x = t.absmax_x;
1330 if (t.absmax_y > cmaxs_y)
1331 cmaxs_y = t.absmax_y;
1332 if (t.absmax_z > cmaxs_z)
1333 cmaxs_z = t.absmax_z;
1340 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1341 if two doors touch, they are assumed to be connected and operate as a unit.
1343 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1345 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).
1347 GOLD_KEY causes the door to open only if the activator holds a gold key.
1349 SILVER_KEY causes the door to open only if the activator holds a silver key.
1351 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1352 "angle" determines the opening direction
1353 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1354 "health" if set, door must be shot open
1355 "speed" movement speed (100 default)
1356 "wait" wait before returning (3 default, -1 = never return)
1357 "lip" lip remaining at end of move (8 default)
1358 "dmg" damage to inflict when blocked (2 default)
1365 FIXME: only one sound set available at the time being
1369 void door_init_startopen()
1371 setorigin (self, self.pos2);
1372 self.pos2 = self.pos1;
1373 self.pos1 = self.origin;
1378 setorigin(self, self.pos1);
1379 self.velocity = '0 0 0';
1380 self.state = STATE_BOTTOM;
1381 self.think = SUB_Null;
1384 // spawnflags require key (for now only func_door)
1385 #define SPAWNFLAGS_GOLD_KEY 8
1386 #define SPAWNFLAGS_SILVER_KEY 16
1387 void spawnfunc_func_door()
1389 // Quake 1 keys compatibility
1390 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1391 self.itemkeys |= ITEM_KEY_BIT(0);
1392 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1393 self.itemkeys |= ITEM_KEY_BIT(1);
1395 //if (!self.deathtype) // map makers can override this
1396 // self.deathtype = " got in the way";
1399 self.max_health = self.health;
1400 if not(InitMovingBrushTrigger())
1402 self.effects |= EF_LOWPRECISION;
1403 self.classname = "door";
1405 self.blocked = door_blocked;
1406 self.use = door_use;
1408 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1409 // if(self.spawnflags & 8)
1410 // self.dmg = 10000;
1412 if(self.dmg && (!self.message))
1413 self.message = "was squished";
1414 if(self.dmg && (!self.message2))
1415 self.message2 = "was squished by";
1417 if (self.sounds > 0)
1419 precache_sound ("plats/medplat1.wav");
1420 precache_sound ("plats/medplat2.wav");
1421 self.noise2 = "plats/medplat1.wav";
1422 self.noise1 = "plats/medplat2.wav";
1432 self.pos1 = self.origin;
1433 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1435 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1436 // but spawn in the open position
1437 if (self.spawnflags & DOOR_START_OPEN)
1438 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1440 self.state = STATE_BOTTOM;
1444 self.takedamage = DAMAGE_YES;
1445 self.event_damage = door_damage;
1451 self.touch = door_touch;
1453 // LinkDoors can't be done until all of the doors have been spawned, so
1454 // the sizes can be detected properly.
1455 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1457 self.reset = door_reset;
1460 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1461 if two doors touch, they are assumed to be connected and operate as a unit.
1463 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1465 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1466 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1467 must have set trigger_reverse to 1.
1468 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1470 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).
1472 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1473 "angle" determines the destination angle for opening. negative values reverse the direction.
1474 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1475 "health" if set, door must be shot open
1476 "speed" movement speed (100 default)
1477 "wait" wait before returning (3 default, -1 = never return)
1478 "dmg" damage to inflict when blocked (2 default)
1485 FIXME: only one sound set available at the time being
1488 void door_rotating_reset()
1490 self.angles = self.pos1;
1491 self.avelocity = '0 0 0';
1492 self.state = STATE_BOTTOM;
1493 self.think = SUB_Null;
1496 void door_rotating_init_startopen()
1498 self.angles = self.movedir;
1499 self.pos2 = '0 0 0';
1500 self.pos1 = self.movedir;
1504 void spawnfunc_func_door_rotating()
1507 //if (!self.deathtype) // map makers can override this
1508 // self.deathtype = " got in the way";
1510 // I abuse "movedir" for denoting the axis for now
1511 if (self.spawnflags & 64) // X (untested)
1512 self.movedir = '0 0 1';
1513 else if (self.spawnflags & 128) // Y (untested)
1514 self.movedir = '1 0 0';
1516 self.movedir = '0 1 0';
1518 if (self.angles_y==0) self.angles_y = 90;
1520 self.movedir = self.movedir * self.angles_y;
1521 self.angles = '0 0 0';
1523 self.max_health = self.health;
1524 self.avelocity = self.movedir;
1525 if not(InitMovingBrushTrigger())
1527 self.velocity = '0 0 0';
1528 //self.effects |= EF_LOWPRECISION;
1529 self.classname = "door_rotating";
1531 self.blocked = door_blocked;
1532 self.use = door_use;
1534 if(self.spawnflags & 8)
1537 if(self.dmg && (!self.message))
1538 self.message = "was squished";
1539 if(self.dmg && (!self.message2))
1540 self.message2 = "was squished by";
1542 if (self.sounds > 0)
1544 precache_sound ("plats/medplat1.wav");
1545 precache_sound ("plats/medplat2.wav");
1546 self.noise2 = "plats/medplat1.wav";
1547 self.noise1 = "plats/medplat2.wav";
1554 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1556 self.pos1 = '0 0 0';
1557 self.pos2 = self.movedir;
1559 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1560 // but spawn in the open position
1561 if (self.spawnflags & DOOR_START_OPEN)
1562 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1564 self.state = STATE_BOTTOM;
1568 self.takedamage = DAMAGE_YES;
1569 self.event_damage = door_damage;
1575 self.touch = door_touch;
1577 // LinkDoors can't be done until all of the doors have been spawned, so
1578 // the sizes can be detected properly.
1579 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1581 self.reset = door_rotating_reset;
1585 =============================================================================
1589 =============================================================================
1592 void() fd_secret_move1;
1593 void() fd_secret_move2;
1594 void() fd_secret_move3;
1595 void() fd_secret_move4;
1596 void() fd_secret_move5;
1597 void() fd_secret_move6;
1598 void() fd_secret_done;
1600 float SECRET_OPEN_ONCE = 1; // stays open
1601 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1602 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1603 float SECRET_NO_SHOOT = 8; // only opened by trigger
1604 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1607 void fd_secret_use()
1610 string message_save;
1612 self.health = 10000;
1613 self.bot_attack = TRUE;
1615 // exit if still moving around...
1616 if (self.origin != self.oldorigin)
1619 message_save = self.message;
1620 self.message = ""; // no more message
1621 SUB_UseTargets(); // fire all targets / killtargets
1622 self.message = message_save;
1624 self.velocity = '0 0 0';
1626 // Make a sound, wait a little...
1628 if (self.noise1 != "")
1629 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1630 self.nextthink = self.ltime + 0.1;
1632 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1633 makevectors(self.mangle);
1637 if (self.spawnflags & SECRET_1ST_DOWN)
1638 self.t_width = fabs(v_up * self.size);
1640 self.t_width = fabs(v_right * self.size);
1644 self.t_length = fabs(v_forward * self.size);
1646 if (self.spawnflags & SECRET_1ST_DOWN)
1647 self.dest1 = self.origin - v_up * self.t_width;
1649 self.dest1 = self.origin + v_right * (self.t_width * temp);
1651 self.dest2 = self.dest1 + v_forward * self.t_length;
1652 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1653 if (self.noise2 != "")
1654 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1657 // Wait after first movement...
1658 void fd_secret_move1()
1660 self.nextthink = self.ltime + 1.0;
1661 self.think = fd_secret_move2;
1662 if (self.noise3 != "")
1663 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1666 // Start moving sideways w/sound...
1667 void fd_secret_move2()
1669 if (self.noise2 != "")
1670 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1671 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1674 // Wait here until time to go back...
1675 void fd_secret_move3()
1677 if (self.noise3 != "")
1678 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1679 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1681 self.nextthink = self.ltime + self.wait;
1682 self.think = fd_secret_move4;
1687 void fd_secret_move4()
1689 if (self.noise2 != "")
1690 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1691 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1695 void fd_secret_move5()
1697 self.nextthink = self.ltime + 1.0;
1698 self.think = fd_secret_move6;
1699 if (self.noise3 != "")
1700 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1703 void fd_secret_move6()
1705 if (self.noise2 != "")
1706 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1707 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1710 void fd_secret_done()
1712 if (self.spawnflags&SECRET_YES_SHOOT)
1714 self.health = 10000;
1715 self.takedamage = DAMAGE_YES;
1716 //self.th_pain = fd_secret_use;
1718 if (self.noise3 != "")
1719 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1722 void secret_blocked()
1724 if (time < self.attack_finished_single)
1726 self.attack_finished_single = time + 0.5;
1727 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1739 if not(other.iscreature)
1741 if (self.attack_finished_single > time)
1744 self.attack_finished_single = time + 2;
1748 if (other.flags & FL_CLIENT)
1749 centerprint (other, self.message);
1750 play2(other, "misc/talk.wav");
1756 if (self.spawnflags&SECRET_YES_SHOOT)
1758 self.health = 10000;
1759 self.takedamage = DAMAGE_YES;
1761 setorigin(self, self.oldorigin);
1762 self.think = SUB_Null;
1765 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1766 Basic secret door. Slides back, then to the side. Angle determines direction.
1767 wait = # of seconds before coming back
1768 1st_left = 1st move is left of arrow
1769 1st_down = 1st move is down from arrow
1770 always_shoot = even if targeted, keep shootable
1771 t_width = override WIDTH to move back (or height if going down)
1772 t_length = override LENGTH to move sideways
1773 "dmg" damage to inflict when blocked (2 default)
1775 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1782 void spawnfunc_func_door_secret()
1784 /*if (!self.deathtype) // map makers can override this
1785 self.deathtype = " got in the way";*/
1791 self.mangle = self.angles;
1792 self.angles = '0 0 0';
1793 self.classname = "door";
1794 if not(InitMovingBrushTrigger())
1796 self.effects |= EF_LOWPRECISION;
1798 self.touch = secret_touch;
1799 self.blocked = secret_blocked;
1801 self.use = fd_secret_use;
1806 self.spawnflags |= SECRET_YES_SHOOT;
1808 if(self.spawnflags&SECRET_YES_SHOOT)
1810 self.health = 10000;
1811 self.takedamage = DAMAGE_YES;
1812 self.event_damage = fd_secret_use;
1814 self.oldorigin = self.origin;
1816 self.wait = 5; // 5 seconds before closing
1818 self.reset = secret_reset;
1822 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1823 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1824 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
1825 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1826 height: amplitude modifier (default 32)
1827 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1828 noise: path/name of looping .wav file to play.
1829 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1833 void func_fourier_controller_think()
1838 self.nextthink = time + 0.1;
1839 if not (self.owner.active == ACTIVE_ACTIVE)
1841 self.owner.velocity = '0 0 0';
1846 n = floor((tokenize_console(self.owner.netname)) / 5);
1847 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1849 v = self.owner.destvec;
1851 for(i = 0; i < n; ++i)
1853 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1854 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;
1857 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1858 // * 10 so it will arrive in 0.1 sec
1859 self.owner.velocity = (v - self.owner.origin) * 10;
1862 void spawnfunc_func_fourier()
1865 if (self.noise != "")
1867 precache_sound(self.noise);
1868 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1875 self.destvec = self.origin;
1876 self.cnt = 360 / self.speed;
1878 self.blocked = generic_plat_blocked;
1879 if(self.dmg & (!self.message))
1880 self.message = " was squished";
1881 if(self.dmg && (!self.message2))
1882 self.message2 = "was squished by";
1883 if(self.dmg && (!self.dmgtime))
1884 self.dmgtime = 0.25;
1885 self.dmgtime2 = time;
1887 if(self.netname == "")
1888 self.netname = "1 0 0 0 1";
1890 if not(InitMovingBrushTrigger())
1893 self.active = ACTIVE_ACTIVE;
1895 // wait for targets to spawn
1896 controller = spawn();
1897 controller.classname = "func_fourier_controller";
1898 controller.owner = self;
1899 controller.nextthink = time + 1;
1900 controller.think = func_fourier_controller_think;
1901 self.nextthink = self.ltime + 999999999;
1902 self.think = SUB_Null;
1904 // Savage: Reduce bandwith, critical on e.g. nexdm02
1905 self.effects |= EF_LOWPRECISION;
1907 // TODO make a reset function for this one
1910 // reusing some fields havocbots declared
1911 .entity wp00, wp01, wp02, wp03;
1913 .float targetfactor, target2factor, target3factor, target4factor;
1914 .vector targetnormal, target2normal, target3normal, target4normal;
1916 vector func_vectormamamam_origin(entity o, float t)
1928 p = e.origin + t * e.velocity;
1930 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1932 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1938 p = e.origin + t * e.velocity;
1940 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1942 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1948 p = e.origin + t * e.velocity;
1950 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1952 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1958 p = e.origin + t * e.velocity;
1960 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1962 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1968 void func_vectormamamam_controller_think()
1970 self.nextthink = time + 0.1;
1972 if not (self.owner.active == ACTIVE_ACTIVE)
1974 self.owner.velocity = '0 0 0';
1978 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1979 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1982 void func_vectormamamam_findtarget()
1984 if(self.target != "")
1985 self.wp00 = find(world, targetname, self.target);
1987 if(self.target2 != "")
1988 self.wp01 = find(world, targetname, self.target2);
1990 if(self.target3 != "")
1991 self.wp02 = find(world, targetname, self.target3);
1993 if(self.target4 != "")
1994 self.wp03 = find(world, targetname, self.target4);
1996 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1997 objerror("No reference entity found, so there is nothing to move. Aborting.");
1999 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2002 controller = spawn();
2003 controller.classname = "func_vectormamamam_controller";
2004 controller.owner = self;
2005 controller.nextthink = time + 1;
2006 controller.think = func_vectormamamam_controller_think;
2009 void spawnfunc_func_vectormamamam()
2011 if (self.noise != "")
2013 precache_sound(self.noise);
2014 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2017 if(!self.targetfactor)
2018 self.targetfactor = 1;
2020 if(!self.target2factor)
2021 self.target2factor = 1;
2023 if(!self.target3factor)
2024 self.target3factor = 1;
2026 if(!self.target4factor)
2027 self.target4factor = 1;
2029 if(vlen(self.targetnormal))
2030 self.targetnormal = normalize(self.targetnormal);
2032 if(vlen(self.target2normal))
2033 self.target2normal = normalize(self.target2normal);
2035 if(vlen(self.target3normal))
2036 self.target3normal = normalize(self.target3normal);
2038 if(vlen(self.target4normal))
2039 self.target4normal = normalize(self.target4normal);
2041 self.blocked = generic_plat_blocked;
2042 if(self.dmg & (!self.message))
2043 self.message = " was squished";
2044 if(self.dmg && (!self.message2))
2045 self.message2 = "was squished by";
2046 if(self.dmg && (!self.dmgtime))
2047 self.dmgtime = 0.25;
2048 self.dmgtime2 = time;
2050 if(self.netname == "")
2051 self.netname = "1 0 0 0 1";
2053 if not(InitMovingBrushTrigger())
2056 // wait for targets to spawn
2057 self.nextthink = self.ltime + 999999999;
2058 self.think = SUB_Null;
2060 // Savage: Reduce bandwith, critical on e.g. nexdm02
2061 self.effects |= EF_LOWPRECISION;
2063 self.active = ACTIVE_ACTIVE;
2065 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2068 void conveyor_think()
2072 // set myself as current conveyor where possible
2073 for(e = world; (e = findentity(e, conveyor, self)); )
2078 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5); e; e = e.chain)
2079 if(!e.conveyor.state)
2082 vector emin = e.absmin;
2083 vector emax = e.absmax;
2084 if(self.solid == SOLID_BSP)
2089 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2090 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2094 for(e = world; (e = findentity(e, conveyor, self)); )
2096 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2097 continue; // done in SV_PlayerPhysics
2099 setorigin(e, e.origin + self.movedir * sys_frametime);
2100 move_out_of_solid(e);
2101 UpdateCSQCProjectile(e);
2103 // stupid conveyor code
2104 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2105 if(trace_fraction > 0)
2106 setorigin(e, trace_endpos);
2111 self.nextthink = time;
2116 self.state = !self.state;
2119 void conveyor_reset()
2121 self.state = (self.spawnflags & 1);
2124 void conveyor_init()
2128 self.movedir = self.movedir * self.speed;
2129 self.think = conveyor_think;
2130 self.nextthink = time;
2133 self.use = conveyor_use;
2134 self.reset = conveyor_reset;
2141 void spawnfunc_trigger_conveyor()
2148 void spawnfunc_func_conveyor()
2151 InitMovingBrushTrigger();
2152 self.movetype = MOVETYPE_NONE;