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;
60 setsize (trigger, tmin, tmax);
65 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
67 self.think = plat_go_down;
68 self.nextthink = self.ltime + 3;
71 void plat_hit_bottom()
73 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
79 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
81 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
86 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
88 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
91 void plat_center_touch()
93 if not(other.iscreature)
96 if (other.health <= 0)
102 else if (self.state == 1)
103 self.nextthink = self.ltime + 1; // delay going down
106 void plat_outside_touch()
108 if not(other.iscreature)
111 if (other.health <= 0)
119 void plat_trigger_use()
122 return; // already activated
129 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
130 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
132 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
133 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
134 // Gib dead/dying stuff
135 if(other.deadflag != DEAD_NO)
136 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
141 else if (self.state == 3)
143 // when in other states, then the plat_crush event came delayed after
144 // plat state already had changed
145 // this isn't a bug per se!
153 objerror ("plat_use: not in up state");
157 .string sound1, sound2;
163 setorigin (self, self.pos1);
169 setorigin (self, self.pos2);
171 self.use = plat_trigger_use;
175 void spawnfunc_path_corner() { }
176 void spawnfunc_func_plat()
183 if (self.sounds == 0)
186 if(self.spawnflags & 4)
189 if(self.dmg && (!self.message))
190 self.message = "was squished";
191 if(self.dmg && (!self.message2))
192 self.message2 = "was squished by";
194 if (self.sounds == 1)
196 precache_sound ("plats/plat1.wav");
197 precache_sound ("plats/plat2.wav");
198 self.noise = "plats/plat1.wav";
199 self.noise1 = "plats/plat2.wav";
202 if (self.sounds == 2)
204 precache_sound ("plats/medplat1.wav");
205 precache_sound ("plats/medplat2.wav");
206 self.noise = "plats/medplat1.wav";
207 self.noise1 = "plats/medplat2.wav";
212 precache_sound (self.sound1);
213 self.noise = self.sound1;
217 precache_sound (self.sound2);
218 self.noise1 = self.sound2;
221 self.mangle = self.angles;
222 self.angles = '0 0 0';
224 self.classname = "plat";
225 if not(InitMovingBrushTrigger())
227 self.effects |= EF_LOWPRECISION;
228 setsize (self, self.mins , self.maxs);
230 self.blocked = plat_crush;
237 self.pos1 = self.origin;
238 self.pos2 = self.origin;
239 self.pos2_z = self.origin_z - self.size_z + self.lip;
241 plat_spawn_inside_trigger (); // the "start moving" trigger
243 self.reset = plat_reset;
252 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
260 self.think = train_next;
261 self.nextthink = self.ltime + self.wait;
275 targ = find(world, targetname, self.target);
277 self.target = targ.target;
279 objerror("train_next: no next target");
280 self.wait = targ.wait;
285 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
287 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
290 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
293 void func_train_find()
296 targ = find(world, targetname, self.target);
297 self.target = targ.target;
299 objerror("func_train_find: no next target");
300 setorigin(self, targ.origin - self.mins);
301 self.nextthink = self.ltime + 1;
302 self.think = train_next;
305 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
306 Ridable platform, targets spawnfunc_path_corner path to follow.
307 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
308 target : targetname of first spawnfunc_path_corner (starts here)
310 void spawnfunc_func_train()
312 if (self.noise != "")
313 precache_sound(self.noise);
316 objerror("func_train without a target");
320 if not(InitMovingBrushTrigger())
322 self.effects |= EF_LOWPRECISION;
324 // wait for targets to spawn
325 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
327 self.blocked = generic_plat_blocked;
328 if(self.dmg & (!self.message))
329 self.message = " was squished";
330 if(self.dmg && (!self.message2))
331 self.message2 = "was squished by";
332 if(self.dmg && (!self.dmgtime))
334 self.dmgtime2 = time;
336 // TODO make a reset function for this one
339 void func_rotating_setactive(float astate)
342 if (astate == ACTIVE_TOGGLE)
344 if(self.active == ACTIVE_ACTIVE)
345 self.active = ACTIVE_NOT;
347 self.active = ACTIVE_ACTIVE;
350 self.active = astate;
352 if(self.active == ACTIVE_NOT)
353 self.avelocity = '0 0 0';
355 self.avelocity = self.pos1;
358 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
359 Brush model that spins in place on one axis (default Z).
360 speed : speed to rotate (in degrees per second)
361 noise : path/name of looping .wav file to play.
362 dmg : Do this mutch dmg every .dmgtime intervall when blocked
366 void spawnfunc_func_rotating()
368 if (self.noise != "")
370 precache_sound(self.noise);
371 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
374 self.active = ACTIVE_ACTIVE;
375 self.setactive = func_rotating_setactive;
379 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
380 if (self.spawnflags & 4) // X (untested)
381 self.avelocity = '0 0 1' * self.speed;
382 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
383 else if (self.spawnflags & 8) // Y (untested)
384 self.avelocity = '1 0 0' * self.speed;
385 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
387 self.avelocity = '0 1 0' * self.speed;
389 self.pos1 = self.avelocity;
391 if(self.dmg & (!self.message))
392 self.message = " was squished";
393 if(self.dmg && (!self.message2))
394 self.message2 = "was squished by";
397 if(self.dmg && (!self.dmgtime))
400 self.dmgtime2 = time;
402 if not(InitMovingBrushTrigger())
404 // no EF_LOWPRECISION here, as rounding angles is bad
406 self.blocked = generic_plat_blocked;
408 // wait for targets to spawn
409 self.nextthink = self.ltime + 999999999;
410 self.think = SUB_Null;
412 // TODO make a reset function for this one
416 void func_bobbing_controller_think()
419 self.nextthink = time + 0.1;
421 if not (self.owner.active == ACTIVE_ACTIVE)
423 self.owner.velocity = '0 0 0';
427 // calculate sinewave using makevectors
428 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
429 v = self.owner.destvec + self.owner.movedir * v_forward_y;
430 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
431 // * 10 so it will arrive in 0.1 sec
432 self.owner.velocity = (v - self.owner.origin) * 10;
435 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
436 Brush model that moves back and forth on one axis (default Z).
437 speed : how long one cycle takes in seconds (default 4)
438 height : how far the cycle moves (default 32)
439 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
440 noise : path/name of looping .wav file to play.
441 dmg : Do this mutch dmg every .dmgtime intervall when blocked
444 void spawnfunc_func_bobbing()
447 if (self.noise != "")
449 precache_sound(self.noise);
450 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
456 // center of bobbing motion
457 self.destvec = self.origin;
458 // time scale to get degrees
459 self.cnt = 360 / self.speed;
461 self.active = ACTIVE_ACTIVE;
463 // damage when blocked
464 self.blocked = generic_plat_blocked;
465 if(self.dmg & (!self.message))
466 self.message = " was squished";
467 if(self.dmg && (!self.message2))
468 self.message2 = "was squished by";
469 if(self.dmg && (!self.dmgtime))
471 self.dmgtime2 = time;
474 if (self.spawnflags & 1) // X
475 self.movedir = '1 0 0' * self.height;
476 else if (self.spawnflags & 2) // Y
477 self.movedir = '0 1 0' * self.height;
479 self.movedir = '0 0 1' * self.height;
481 if not(InitMovingBrushTrigger())
484 // wait for targets to spawn
485 controller = spawn();
486 controller.classname = "func_bobbing_controller";
487 controller.owner = self;
488 controller.nextthink = time + 1;
489 controller.think = func_bobbing_controller_think;
490 self.nextthink = self.ltime + 999999999;
491 self.think = SUB_Null;
493 // Savage: Reduce bandwith, critical on e.g. nexdm02
494 self.effects |= EF_LOWPRECISION;
496 // TODO make a reset function for this one
500 void func_pendulum_controller_think()
503 self.nextthink = time + 0.1;
505 if not (self.owner.active == ACTIVE_ACTIVE)
507 self.owner.avelocity_x = 0;
511 // calculate sinewave using makevectors
512 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
513 v = self.owner.speed * v_forward_y + self.cnt;
514 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
516 // * 10 so it will arrive in 0.1 sec
517 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
521 void spawnfunc_func_pendulum()
524 if (self.noise != "")
526 precache_sound(self.noise);
527 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
530 self.active = ACTIVE_ACTIVE;
532 // keys: angle, speed, phase, noise, freq
536 // not initializing self.dmg to 2, to allow damageless pendulum
538 if(self.dmg & (!self.message))
539 self.message = " was squished";
540 if(self.dmg && (!self.message2))
541 self.message2 = "was squished by";
542 if(self.dmg && (!self.dmgtime))
544 self.dmgtime2 = time;
546 self.blocked = generic_plat_blocked;
548 self.avelocity_z = 0.0000001;
549 if not(InitMovingBrushTrigger())
554 // find pendulum length (same formula as Q3A)
555 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
558 // copy initial angle
559 self.cnt = self.angles_z;
561 // wait for targets to spawn
562 controller = spawn();
563 controller.classname = "func_pendulum_controller";
564 controller.owner = self;
565 controller.nextthink = time + 1;
566 controller.think = func_pendulum_controller_think;
567 self.nextthink = self.ltime + 999999999;
568 self.think = SUB_Null;
570 //self.effects |= EF_LOWPRECISION;
572 // TODO make a reset function for this one
575 // button and multiple button
578 void() button_return;
582 self.state = STATE_TOP;
583 self.nextthink = self.ltime + self.wait;
584 self.think = button_return;
585 activator = self.enemy;
587 self.frame = 1; // use alternate textures
592 self.state = STATE_BOTTOM;
597 self.state = STATE_DOWN;
598 SUB_CalcMove (self.pos1, self.speed, button_done);
599 self.frame = 0; // use normal textures
601 self.takedamage = DAMAGE_YES; // can be shot again
605 void button_blocked()
607 // do nothing, just don't come all the way back out
613 self.health = self.max_health;
614 self.takedamage = DAMAGE_NO; // will be reset upon return
616 if (self.state == STATE_UP || self.state == STATE_TOP)
619 if (self.noise != "")
620 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
622 self.state = STATE_UP;
623 SUB_CalcMove (self.pos2, self.speed, button_wait);
628 self.health = self.max_health;
629 setorigin(self, self.pos1);
630 self.frame = 0; // use normal textures
631 self.state = STATE_BOTTOM;
633 self.takedamage = DAMAGE_YES; // can be shot again
638 // if (activator.classname != "player")
640 // dprint(activator.classname);
641 // dprint(" triggered a button\n");
644 if not (self.active == ACTIVE_ACTIVE)
647 self.enemy = activator;
653 // if (activator.classname != "player")
655 // dprint(activator.classname);
656 // dprint(" touched a button\n");
660 if not(other.iscreature)
662 if(other.velocity * self.movedir < 0)
666 self.enemy = other.owner;
670 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
672 if(self.spawnflags & DOOR_NOSPLASH)
673 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
675 self.health = self.health - damage;
676 if (self.health <= 0)
678 // if (activator.classname != "player")
680 // dprint(activator.classname);
681 // dprint(" killed a button\n");
683 self.enemy = damage_attacker;
689 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
690 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.
692 "angle" determines the opening direction
693 "target" all entities with a matching targetname will be used
694 "speed" override the default 40 speed
695 "wait" override the default 1 second wait (-1 = never return)
696 "lip" override the default 4 pixel lip remaining at end of move
697 "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
704 void spawnfunc_func_button()
708 if not(InitMovingBrushTrigger())
710 self.effects |= EF_LOWPRECISION;
712 self.blocked = button_blocked;
713 self.use = button_use;
715 // if (self.health == 0) // all buttons are now shootable
719 self.max_health = self.health;
720 self.event_damage = button_damage;
721 self.takedamage = DAMAGE_YES;
724 self.touch = button_touch;
734 precache_sound(self.noise);
736 self.active = ACTIVE_ACTIVE;
738 self.pos1 = self.origin;
739 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
740 self.flags |= FL_NOTARGET;
746 float DOOR_START_OPEN = 1;
747 float DOOR_DONT_LINK = 4;
748 float DOOR_TOGGLE = 32;
752 Doors are similar to buttons, but can spawn a fat trigger field around them
753 to open without a touch, and they link together to form simultanious
756 Door.owner is the master door. If there is only one door, it points to itself.
757 If multiple doors, all will point to a single one.
759 Door.enemy chains from the master door through all doors linked in the chain.
764 =============================================================================
768 =============================================================================
773 void() door_rotating_go_down;
774 void() door_rotating_go_up;
779 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
780 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
783 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
784 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
786 //Dont chamge direction for dead or dying stuff
787 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
790 if (self.state == STATE_DOWN)
791 if (self.classname == "door")
796 door_rotating_go_up ();
799 if (self.classname == "door")
804 door_rotating_go_down ();
808 //gib dying stuff just to make sure
809 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
810 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
814 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
815 // if a door has a negative wait, it would never come back if blocked,
816 // so let it just squash the object to death real fast
817 /* if (self.wait >= 0)
819 if (self.state == STATE_DOWN)
830 if (self.noise1 != "")
831 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
832 self.state = STATE_TOP;
833 if (self.spawnflags & DOOR_TOGGLE)
834 return; // don't come down automatically
835 if (self.classname == "door")
837 self.think = door_go_down;
840 self.think = door_rotating_go_down;
842 self.nextthink = self.ltime + self.wait;
845 void door_hit_bottom()
847 if (self.noise1 != "")
848 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
849 self.state = STATE_BOTTOM;
854 if (self.noise2 != "")
855 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
858 self.takedamage = DAMAGE_YES;
859 self.health = self.max_health;
862 self.state = STATE_DOWN;
863 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
868 if (self.state == STATE_UP)
869 return; // already going up
871 if (self.state == STATE_TOP)
872 { // reset top wait time
873 self.nextthink = self.ltime + self.wait;
877 if (self.noise2 != "")
878 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
879 self.state = STATE_UP;
880 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
883 oldmessage = self.message;
886 self.message = oldmessage;
892 =============================================================================
896 =============================================================================
899 float door_check_keys(void) {
909 if not(door.itemkeys)
912 // this door require a key
913 // only a player can have a key
914 if (other.classname != "player")
917 if (item_keys_usekey(door, other)) {
918 // some keys were used
919 if (other.key_door_messagetime <= time) {
920 play2(other, "misc/talk.wav");
921 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
922 other.key_door_messagetime = time + 2;
926 if (other.key_door_messagetime <= time) {
927 play2(other, "misc/talk.wav");
928 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
929 other.key_door_messagetime = time + 2;
934 // door is now unlocked
935 play2(other, "misc/talk.wav");
936 centerprint(other, "Door unlocked!");
948 if (self.owner != self)
949 objerror ("door_fire: self.owner != self");
953 if (self.spawnflags & DOOR_TOGGLE)
955 if (self.state == STATE_UP || self.state == STATE_TOP)
960 if (self.classname == "door")
966 door_rotating_go_down ();
969 } while ( (self != starte) && (self != world) );
975 // trigger all paired doors
979 if (self.classname == "door")
984 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
985 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
987 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
988 self.pos2 = '0 0 0' - self.pos2;
990 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
991 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
992 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
994 door_rotating_go_up ();
998 } while ( (self != starte) && (self != world) );
1007 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1019 void door_trigger_touch()
1021 if (other.health < 1)
1022 if not(other.iscreature && other.deadflag == DEAD_NO)
1025 if (time < self.attack_finished_single)
1028 // check if door is locked
1029 if (!door_check_keys())
1032 self.attack_finished_single = time + 1;
1041 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1044 if(self.spawnflags & DOOR_NOSPLASH)
1045 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1047 self.health = self.health - damage;
1049 if (self.itemkeys) {
1050 // don't allow opening doors through damage if keys are required
1054 if (self.health <= 0)
1058 self.health = self.max_health;
1059 self.takedamage = DAMAGE_NO; // wil be reset upon return
1075 if(other.classname != "player")
1077 if (self.owner.attack_finished_single > time)
1080 self.owner.attack_finished_single = time + 2;
1082 if (!(self.owner.dmg) && (self.owner.message != ""))
1084 if (other.flags & FL_CLIENT)
1085 centerprint (other, self.owner.message);
1086 play2(other, "misc/talk.wav");
1091 void door_generic_plat_blocked()
1094 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1095 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1098 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1099 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1101 //Dont chamge direction for dead or dying stuff
1102 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1105 if (self.state == STATE_DOWN)
1106 door_rotating_go_up ();
1108 door_rotating_go_down ();
1111 //gib dying stuff just to make sure
1112 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1113 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1117 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1118 // if a door has a negative wait, it would never come back if blocked,
1119 // so let it just squash the object to death real fast
1120 /* if (self.wait >= 0)
1122 if (self.state == STATE_DOWN)
1123 door_rotating_go_up ();
1125 door_rotating_go_down ();
1131 void door_rotating_hit_top()
1133 if (self.noise1 != "")
1134 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1135 self.state = STATE_TOP;
1136 if (self.spawnflags & DOOR_TOGGLE)
1137 return; // don't come down automatically
1138 self.think = door_rotating_go_down;
1139 self.nextthink = self.ltime + self.wait;
1142 void door_rotating_hit_bottom()
1144 if (self.noise1 != "")
1145 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1146 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1148 self.pos2 = '0 0 0' - self.pos2;
1151 self.state = STATE_BOTTOM;
1154 void door_rotating_go_down()
1156 if (self.noise2 != "")
1157 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1158 if (self.max_health)
1160 self.takedamage = DAMAGE_YES;
1161 self.health = self.max_health;
1164 self.state = STATE_DOWN;
1165 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1168 void door_rotating_go_up()
1170 if (self.state == STATE_UP)
1171 return; // already going up
1173 if (self.state == STATE_TOP)
1174 { // reset top wait time
1175 self.nextthink = self.ltime + self.wait;
1178 if (self.noise2 != "")
1179 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1180 self.state = STATE_UP;
1181 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1184 oldmessage = self.message;
1187 self.message = oldmessage;
1194 =============================================================================
1198 =============================================================================
1202 entity spawn_field(vector fmins, vector fmaxs)
1208 trigger.classname = "doortriggerfield";
1209 trigger.movetype = MOVETYPE_NONE;
1210 trigger.solid = SOLID_TRIGGER;
1211 trigger.owner = self;
1212 trigger.touch = door_trigger_touch;
1216 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1221 float EntitiesTouching(entity e1, entity e2)
1223 if (e1.absmin_x > e2.absmax_x)
1225 if (e1.absmin_y > e2.absmax_y)
1227 if (e1.absmin_z > e2.absmax_z)
1229 if (e1.absmax_x < e2.absmin_x)
1231 if (e1.absmax_y < e2.absmin_y)
1233 if (e1.absmax_z < e2.absmin_z)
1249 vector cmins, cmaxs;
1252 return; // already linked by another door
1253 if (self.spawnflags & 4)
1255 self.owner = self.enemy = self;
1263 self.trigger_field = spawn_field(self.absmin, self.absmax);
1265 return; // don't want to link this door
1268 cmins = self.absmin;
1269 cmaxs = self.absmax;
1276 self.owner = starte; // master door
1279 starte.health = self.health;
1281 starte.targetname = self.targetname;
1282 if (self.message != "")
1283 starte.message = self.message;
1285 t = find(t, classname, self.classname);
1288 self.enemy = starte; // make the chain a loop
1290 // shootable, or triggered doors just needed the owner/enemy links,
1291 // they don't spawn a field
1302 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1307 if (EntitiesTouching(self,t))
1310 objerror ("cross connected doors");
1315 if (t.absmin_x < cmins_x)
1316 cmins_x = t.absmin_x;
1317 if (t.absmin_y < cmins_y)
1318 cmins_y = t.absmin_y;
1319 if (t.absmin_z < cmins_z)
1320 cmins_z = t.absmin_z;
1321 if (t.absmax_x > cmaxs_x)
1322 cmaxs_x = t.absmax_x;
1323 if (t.absmax_y > cmaxs_y)
1324 cmaxs_y = t.absmax_y;
1325 if (t.absmax_z > cmaxs_z)
1326 cmaxs_z = t.absmax_z;
1333 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1334 if two doors touch, they are assumed to be connected and operate as a unit.
1336 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1338 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).
1340 GOLD_KEY causes the door to open only if the activator holds a gold key.
1342 SILVER_KEY causes the door to open only if the activator holds a silver key.
1344 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1345 "angle" determines the opening direction
1346 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1347 "health" if set, door must be shot open
1348 "speed" movement speed (100 default)
1349 "wait" wait before returning (3 default, -1 = never return)
1350 "lip" lip remaining at end of move (8 default)
1351 "dmg" damage to inflict when blocked (2 default)
1358 FIXME: only one sound set available at the time being
1362 void door_init_startopen()
1364 setorigin (self, self.pos2);
1365 self.pos2 = self.pos1;
1366 self.pos1 = self.origin;
1371 setorigin(self, self.pos1);
1372 self.velocity = '0 0 0';
1373 self.state = STATE_BOTTOM;
1374 self.think = SUB_Null;
1377 // spawnflags require key (for now only func_door)
1378 #define SPAWNFLAGS_GOLD_KEY 8
1379 #define SPAWNFLAGS_SILVER_KEY 16
1380 void spawnfunc_func_door()
1382 // Quake 1 keys compatibility
1383 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1384 self.itemkeys |= ITEM_KEY_BIT(0);
1385 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1386 self.itemkeys |= ITEM_KEY_BIT(1);
1388 //if (!self.deathtype) // map makers can override this
1389 // self.deathtype = " got in the way";
1392 self.max_health = self.health;
1393 if not(InitMovingBrushTrigger())
1395 self.effects |= EF_LOWPRECISION;
1396 self.classname = "door";
1398 self.blocked = door_blocked;
1399 self.use = door_use;
1401 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1402 // if(self.spawnflags & 8)
1403 // self.dmg = 10000;
1405 if(self.dmg && (!self.message))
1406 self.message = "was squished";
1407 if(self.dmg && (!self.message2))
1408 self.message2 = "was squished by";
1410 if (self.sounds > 0)
1412 precache_sound ("plats/medplat1.wav");
1413 precache_sound ("plats/medplat2.wav");
1414 self.noise2 = "plats/medplat1.wav";
1415 self.noise1 = "plats/medplat2.wav";
1425 self.pos1 = self.origin;
1426 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1428 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1429 // but spawn in the open position
1430 if (self.spawnflags & DOOR_START_OPEN)
1431 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1433 self.state = STATE_BOTTOM;
1437 self.takedamage = DAMAGE_YES;
1438 self.event_damage = door_damage;
1444 self.touch = door_touch;
1446 // LinkDoors can't be done until all of the doors have been spawned, so
1447 // the sizes can be detected properly.
1448 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1450 self.reset = door_reset;
1453 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1454 if two doors touch, they are assumed to be connected and operate as a unit.
1456 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1458 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1459 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1460 must have set trigger_reverse to 1.
1461 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1463 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).
1465 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1466 "angle" determines the destination angle for opening. negative values reverse the direction.
1467 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1468 "health" if set, door must be shot open
1469 "speed" movement speed (100 default)
1470 "wait" wait before returning (3 default, -1 = never return)
1471 "dmg" damage to inflict when blocked (2 default)
1478 FIXME: only one sound set available at the time being
1481 void door_rotating_reset()
1483 self.angles = self.pos1;
1484 self.avelocity = '0 0 0';
1485 self.state = STATE_BOTTOM;
1486 self.think = SUB_Null;
1489 void door_rotating_init_startopen()
1491 self.angles = self.movedir;
1492 self.pos2 = '0 0 0';
1493 self.pos1 = self.movedir;
1497 void spawnfunc_func_door_rotating()
1500 //if (!self.deathtype) // map makers can override this
1501 // self.deathtype = " got in the way";
1503 // I abuse "movedir" for denoting the axis for now
1504 if (self.spawnflags & 64) // X (untested)
1505 self.movedir = '0 0 1';
1506 else if (self.spawnflags & 128) // Y (untested)
1507 self.movedir = '1 0 0';
1509 self.movedir = '0 1 0';
1511 if (self.angles_y==0) self.angles_y = 90;
1513 self.movedir = self.movedir * self.angles_y;
1514 self.angles = '0 0 0';
1516 self.max_health = self.health;
1517 self.avelocity = self.movedir;
1518 if not(InitMovingBrushTrigger())
1520 self.velocity = '0 0 0';
1521 //self.effects |= EF_LOWPRECISION;
1522 self.classname = "door_rotating";
1524 self.blocked = door_blocked;
1525 self.use = door_use;
1527 if(self.spawnflags & 8)
1530 if(self.dmg && (!self.message))
1531 self.message = "was squished";
1532 if(self.dmg && (!self.message2))
1533 self.message2 = "was squished by";
1535 if (self.sounds > 0)
1537 precache_sound ("plats/medplat1.wav");
1538 precache_sound ("plats/medplat2.wav");
1539 self.noise2 = "plats/medplat1.wav";
1540 self.noise1 = "plats/medplat2.wav";
1547 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1549 self.pos1 = '0 0 0';
1550 self.pos2 = self.movedir;
1552 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1553 // but spawn in the open position
1554 if (self.spawnflags & DOOR_START_OPEN)
1555 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1557 self.state = STATE_BOTTOM;
1561 self.takedamage = DAMAGE_YES;
1562 self.event_damage = door_damage;
1568 self.touch = door_touch;
1570 // LinkDoors can't be done until all of the doors have been spawned, so
1571 // the sizes can be detected properly.
1572 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1574 self.reset = door_rotating_reset;
1578 =============================================================================
1582 =============================================================================
1585 void() fd_secret_move1;
1586 void() fd_secret_move2;
1587 void() fd_secret_move3;
1588 void() fd_secret_move4;
1589 void() fd_secret_move5;
1590 void() fd_secret_move6;
1591 void() fd_secret_done;
1593 float SECRET_OPEN_ONCE = 1; // stays open
1594 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1595 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1596 float SECRET_NO_SHOOT = 8; // only opened by trigger
1597 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1600 void fd_secret_use()
1603 string message_save;
1605 self.health = 10000;
1606 self.bot_attack = TRUE;
1608 // exit if still moving around...
1609 if (self.origin != self.oldorigin)
1612 message_save = self.message;
1613 self.message = ""; // no more message
1614 SUB_UseTargets(); // fire all targets / killtargets
1615 self.message = message_save;
1617 self.velocity = '0 0 0';
1619 // Make a sound, wait a little...
1621 if (self.noise1 != "")
1622 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1623 self.nextthink = self.ltime + 0.1;
1625 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1626 makevectors(self.mangle);
1630 if (self.spawnflags & SECRET_1ST_DOWN)
1631 self.t_width = fabs(v_up * self.size);
1633 self.t_width = fabs(v_right * self.size);
1637 self.t_length = fabs(v_forward * self.size);
1639 if (self.spawnflags & SECRET_1ST_DOWN)
1640 self.dest1 = self.origin - v_up * self.t_width;
1642 self.dest1 = self.origin + v_right * (self.t_width * temp);
1644 self.dest2 = self.dest1 + v_forward * self.t_length;
1645 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1646 if (self.noise2 != "")
1647 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1650 // Wait after first movement...
1651 void fd_secret_move1()
1653 self.nextthink = self.ltime + 1.0;
1654 self.think = fd_secret_move2;
1655 if (self.noise3 != "")
1656 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1659 // Start moving sideways w/sound...
1660 void fd_secret_move2()
1662 if (self.noise2 != "")
1663 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1664 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1667 // Wait here until time to go back...
1668 void fd_secret_move3()
1670 if (self.noise3 != "")
1671 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1672 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1674 self.nextthink = self.ltime + self.wait;
1675 self.think = fd_secret_move4;
1680 void fd_secret_move4()
1682 if (self.noise2 != "")
1683 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1684 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1688 void fd_secret_move5()
1690 self.nextthink = self.ltime + 1.0;
1691 self.think = fd_secret_move6;
1692 if (self.noise3 != "")
1693 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1696 void fd_secret_move6()
1698 if (self.noise2 != "")
1699 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1700 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1703 void fd_secret_done()
1705 if (self.spawnflags&SECRET_YES_SHOOT)
1707 self.health = 10000;
1708 self.takedamage = DAMAGE_YES;
1709 //self.th_pain = fd_secret_use;
1711 if (self.noise3 != "")
1712 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1715 void secret_blocked()
1717 if (time < self.attack_finished_single)
1719 self.attack_finished_single = time + 0.5;
1720 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1732 if not(other.iscreature)
1734 if (self.attack_finished_single > time)
1737 self.attack_finished_single = time + 2;
1741 if (other.flags & FL_CLIENT)
1742 centerprint (other, self.message);
1743 play2(other, "misc/talk.wav");
1749 if (self.spawnflags&SECRET_YES_SHOOT)
1751 self.health = 10000;
1752 self.takedamage = DAMAGE_YES;
1754 setorigin(self, self.oldorigin);
1755 self.think = SUB_Null;
1758 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1759 Basic secret door. Slides back, then to the side. Angle determines direction.
1760 wait = # of seconds before coming back
1761 1st_left = 1st move is left of arrow
1762 1st_down = 1st move is down from arrow
1763 always_shoot = even if targeted, keep shootable
1764 t_width = override WIDTH to move back (or height if going down)
1765 t_length = override LENGTH to move sideways
1766 "dmg" damage to inflict when blocked (2 default)
1768 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1775 void spawnfunc_func_door_secret()
1777 /*if (!self.deathtype) // map makers can override this
1778 self.deathtype = " got in the way";*/
1784 self.mangle = self.angles;
1785 self.angles = '0 0 0';
1786 self.classname = "door";
1787 if not(InitMovingBrushTrigger())
1789 self.effects |= EF_LOWPRECISION;
1791 self.touch = secret_touch;
1792 self.blocked = secret_blocked;
1794 self.use = fd_secret_use;
1799 self.spawnflags |= SECRET_YES_SHOOT;
1801 if(self.spawnflags&SECRET_YES_SHOOT)
1803 self.health = 10000;
1804 self.takedamage = DAMAGE_YES;
1805 self.event_damage = fd_secret_use;
1807 self.oldorigin = self.origin;
1809 self.wait = 5; // 5 seconds before closing
1811 self.reset = secret_reset;
1815 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1816 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1817 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
1818 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1819 height: amplitude modifier (default 32)
1820 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1821 noise: path/name of looping .wav file to play.
1822 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1826 void func_fourier_controller_think()
1831 self.nextthink = time + 0.1;
1832 if not (self.owner.active == ACTIVE_ACTIVE)
1834 self.owner.velocity = '0 0 0';
1839 n = floor((tokenize_console(self.owner.netname)) / 5);
1840 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1842 v = self.owner.destvec;
1844 for(i = 0; i < n; ++i)
1846 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1847 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;
1850 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1851 // * 10 so it will arrive in 0.1 sec
1852 self.owner.velocity = (v - self.owner.origin) * 10;
1855 void spawnfunc_func_fourier()
1858 if (self.noise != "")
1860 precache_sound(self.noise);
1861 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1868 self.destvec = self.origin;
1869 self.cnt = 360 / self.speed;
1871 self.blocked = generic_plat_blocked;
1872 if(self.dmg & (!self.message))
1873 self.message = " was squished";
1874 if(self.dmg && (!self.message2))
1875 self.message2 = "was squished by";
1876 if(self.dmg && (!self.dmgtime))
1877 self.dmgtime = 0.25;
1878 self.dmgtime2 = time;
1880 if(self.netname == "")
1881 self.netname = "1 0 0 0 1";
1883 if not(InitMovingBrushTrigger())
1886 self.active = ACTIVE_ACTIVE;
1888 // wait for targets to spawn
1889 controller = spawn();
1890 controller.classname = "func_fourier_controller";
1891 controller.owner = self;
1892 controller.nextthink = time + 1;
1893 controller.think = func_fourier_controller_think;
1894 self.nextthink = self.ltime + 999999999;
1895 self.think = SUB_Null;
1897 // Savage: Reduce bandwith, critical on e.g. nexdm02
1898 self.effects |= EF_LOWPRECISION;
1900 // TODO make a reset function for this one
1903 // reusing some fields havocbots declared
1904 .entity wp00, wp01, wp02, wp03;
1906 .float targetfactor, target2factor, target3factor, target4factor;
1907 .vector targetnormal, target2normal, target3normal, target4normal;
1909 vector func_vectormamamam_origin(entity o, float t)
1921 p = e.origin + t * e.velocity;
1923 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1925 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1931 p = e.origin + t * e.velocity;
1933 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1935 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1941 p = e.origin + t * e.velocity;
1943 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1945 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1951 p = e.origin + t * e.velocity;
1953 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1955 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1961 void func_vectormamamam_controller_think()
1963 self.nextthink = time + 0.1;
1965 if not (self.owner.active == ACTIVE_ACTIVE)
1967 self.owner.velocity = '0 0 0';
1971 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1972 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1975 void func_vectormamamam_findtarget()
1977 if(self.target != "")
1978 self.wp00 = find(world, targetname, self.target);
1980 if(self.target2 != "")
1981 self.wp01 = find(world, targetname, self.target2);
1983 if(self.target3 != "")
1984 self.wp02 = find(world, targetname, self.target3);
1986 if(self.target4 != "")
1987 self.wp03 = find(world, targetname, self.target4);
1989 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
1990 objerror("No reference entity found, so there is nothing to move. Aborting.");
1992 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
1995 controller = spawn();
1996 controller.classname = "func_vectormamamam_controller";
1997 controller.owner = self;
1998 controller.nextthink = time + 1;
1999 controller.think = func_vectormamamam_controller_think;
2002 void spawnfunc_func_vectormamamam()
2004 if (self.noise != "")
2006 precache_sound(self.noise);
2007 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2010 if(!self.targetfactor)
2011 self.targetfactor = 1;
2013 if(!self.target2factor)
2014 self.target2factor = 1;
2016 if(!self.target3factor)
2017 self.target3factor = 1;
2019 if(!self.target4factor)
2020 self.target4factor = 1;
2022 if(vlen(self.targetnormal))
2023 self.targetnormal = normalize(self.targetnormal);
2025 if(vlen(self.target2normal))
2026 self.target2normal = normalize(self.target2normal);
2028 if(vlen(self.target3normal))
2029 self.target3normal = normalize(self.target3normal);
2031 if(vlen(self.target4normal))
2032 self.target4normal = normalize(self.target4normal);
2034 self.blocked = generic_plat_blocked;
2035 if(self.dmg & (!self.message))
2036 self.message = " was squished";
2037 if(self.dmg && (!self.message2))
2038 self.message2 = "was squished by";
2039 if(self.dmg && (!self.dmgtime))
2040 self.dmgtime = 0.25;
2041 self.dmgtime2 = time;
2043 if(self.netname == "")
2044 self.netname = "1 0 0 0 1";
2046 if not(InitMovingBrushTrigger())
2049 // wait for targets to spawn
2050 self.nextthink = self.ltime + 999999999;
2051 self.think = SUB_Null;
2053 // Savage: Reduce bandwith, critical on e.g. nexdm02
2054 self.effects |= EF_LOWPRECISION;
2056 self.active = ACTIVE_ACTIVE;
2058 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2061 void conveyor_think()
2065 // set myself as current conveyor where possible
2066 for(e = world; (e = findentity(e, conveyor, self)); )
2071 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5); e; e = e.chain)
2072 if(!e.conveyor.state)
2073 if(e.movetype != MOVETYPE_NONE)
2075 vector emin = e.absmin;
2076 vector emax = e.absmax;
2077 if(self.solid == SOLID_BSP)
2082 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2083 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2087 for(e = world; (e = findentity(e, conveyor, self)); )
2089 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2090 continue; // done in SV_PlayerPhysics
2092 // stupid conveyor code
2093 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2094 if(trace_fraction > 0)
2095 setorigin(e, trace_endpos);
2099 self.nextthink = time;
2104 self.state = !self.state;
2107 void conveyor_reset()
2109 self.state = (self.spawnflags & 1);
2112 void conveyor_init()
2116 self.movedir = self.movedir * self.speed;
2117 self.think = conveyor_think;
2118 self.nextthink = time;
2121 self.use = conveyor_use;
2122 self.reset = conveyor_reset;
2129 void spawnfunc_trigger_conveyor()
2135 void spawnfunc_func_conveyor()
2138 InitMovingBrushTrigger();
2139 self.movetype = MOVETYPE_NONE;