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, TSPEED_LINEAR, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, TSPEED_LINEAR, 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
254 .float train_wait_turning;
258 // if using bezier curves and turning is enabled, the train will turn toward the next point while waiting
259 if(!self.train_wait_turning)
260 if (self.spawnflags & 1 && self.bezier_turn && self.wait >= 0)
263 targ = find(world, targetname, self.target);
264 SUB_CalcAngleMove(targ.origin, TSPEED_TIME, self.ltime + self.wait, train_wait);
265 self.train_wait_turning = TRUE;
270 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
278 self.think = train_next;
279 self.nextthink = self.ltime + self.wait;
288 self.train_wait_turning = FALSE;
296 targ = find(world, targetname, self.target);
297 self.target = targ.target;
298 if (self.spawnflags & 1)
300 cp = find(world, target, targ.targetname); // get the previous corner first
301 cp = find(world, targetname, cp.curve); // now get its second target (the control point)
302 if(cp.targetname == "")
303 cp_org = targ.origin - self.mins; // no control point found, assume a straight line to the destination
305 cp_org = cp.origin - self.mins;
308 objerror("train_next: no next target");
309 self.wait = targ.wait;
315 if (self.spawnflags & 1)
316 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
318 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
322 if (self.spawnflags & 1)
323 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
325 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
329 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
332 void func_train_find()
335 targ = find(world, targetname, self.target);
336 self.target = targ.target;
338 objerror("func_train_find: no next target");
339 setorigin(self, targ.origin - self.mins);
340 self.nextthink = self.ltime + 1;
341 self.think = train_next;
344 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
345 Ridable platform, targets spawnfunc_path_corner path to follow.
346 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
347 target : targetname of first spawnfunc_path_corner (starts here)
349 void spawnfunc_func_train()
351 if (self.noise != "")
352 precache_sound(self.noise);
355 objerror("func_train without a target");
358 if (self.spawnflags & 2)
359 self.bezier_turn = TRUE;
361 if not(InitMovingBrushTrigger())
363 self.effects |= EF_LOWPRECISION;
365 // wait for targets to spawn
366 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
368 self.blocked = generic_plat_blocked;
369 if(self.dmg & (!self.message))
370 self.message = " was squished";
371 if(self.dmg && (!self.message2))
372 self.message2 = "was squished by";
373 if(self.dmg && (!self.dmgtime))
375 self.dmgtime2 = time;
377 // TODO make a reset function for this one
380 void func_rotating_setactive(float astate)
383 if (astate == ACTIVE_TOGGLE)
385 if(self.active == ACTIVE_ACTIVE)
386 self.active = ACTIVE_NOT;
388 self.active = ACTIVE_ACTIVE;
391 self.active = astate;
393 if(self.active == ACTIVE_NOT)
394 self.avelocity = '0 0 0';
396 self.avelocity = self.pos1;
399 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
400 Brush model that spins in place on one axis (default Z).
401 speed : speed to rotate (in degrees per second)
402 noise : path/name of looping .wav file to play.
403 dmg : Do this mutch dmg every .dmgtime intervall when blocked
407 void spawnfunc_func_rotating()
409 if (self.noise != "")
411 precache_sound(self.noise);
412 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
415 self.active = ACTIVE_ACTIVE;
416 self.setactive = func_rotating_setactive;
420 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
421 if (self.spawnflags & 4) // X (untested)
422 self.avelocity = '0 0 1' * self.speed;
423 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
424 else if (self.spawnflags & 8) // Y (untested)
425 self.avelocity = '1 0 0' * self.speed;
426 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
428 self.avelocity = '0 1 0' * self.speed;
430 self.pos1 = self.avelocity;
432 if(self.dmg & (!self.message))
433 self.message = " was squished";
434 if(self.dmg && (!self.message2))
435 self.message2 = "was squished by";
438 if(self.dmg && (!self.dmgtime))
441 self.dmgtime2 = time;
443 if not(InitMovingBrushTrigger())
445 // no EF_LOWPRECISION here, as rounding angles is bad
447 self.blocked = generic_plat_blocked;
449 // wait for targets to spawn
450 self.nextthink = self.ltime + 999999999;
451 self.think = SUB_Null;
453 // TODO make a reset function for this one
457 void func_bobbing_controller_think()
460 self.nextthink = time + 0.1;
462 if not (self.owner.active == ACTIVE_ACTIVE)
464 self.owner.velocity = '0 0 0';
468 // calculate sinewave using makevectors
469 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
470 v = self.owner.destvec + self.owner.movedir * v_forward_y;
471 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
472 // * 10 so it will arrive in 0.1 sec
473 self.owner.velocity = (v - self.owner.origin) * 10;
476 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
477 Brush model that moves back and forth on one axis (default Z).
478 speed : how long one cycle takes in seconds (default 4)
479 height : how far the cycle moves (default 32)
480 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
481 noise : path/name of looping .wav file to play.
482 dmg : Do this mutch dmg every .dmgtime intervall when blocked
485 void spawnfunc_func_bobbing()
488 if (self.noise != "")
490 precache_sound(self.noise);
491 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
497 // center of bobbing motion
498 self.destvec = self.origin;
499 // time scale to get degrees
500 self.cnt = 360 / self.speed;
502 self.active = ACTIVE_ACTIVE;
504 // damage when blocked
505 self.blocked = generic_plat_blocked;
506 if(self.dmg & (!self.message))
507 self.message = " was squished";
508 if(self.dmg && (!self.message2))
509 self.message2 = "was squished by";
510 if(self.dmg && (!self.dmgtime))
512 self.dmgtime2 = time;
515 if (self.spawnflags & 1) // X
516 self.movedir = '1 0 0' * self.height;
517 else if (self.spawnflags & 2) // Y
518 self.movedir = '0 1 0' * self.height;
520 self.movedir = '0 0 1' * self.height;
522 if not(InitMovingBrushTrigger())
525 // wait for targets to spawn
526 controller = spawn();
527 controller.classname = "func_bobbing_controller";
528 controller.owner = self;
529 controller.nextthink = time + 1;
530 controller.think = func_bobbing_controller_think;
531 self.nextthink = self.ltime + 999999999;
532 self.think = SUB_Null;
534 // Savage: Reduce bandwith, critical on e.g. nexdm02
535 self.effects |= EF_LOWPRECISION;
537 // TODO make a reset function for this one
541 void func_pendulum_controller_think()
544 self.nextthink = time + 0.1;
546 if not (self.owner.active == ACTIVE_ACTIVE)
548 self.owner.avelocity_x = 0;
552 // calculate sinewave using makevectors
553 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
554 v = self.owner.speed * v_forward_y + self.cnt;
555 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
557 // * 10 so it will arrive in 0.1 sec
558 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
562 void spawnfunc_func_pendulum()
565 if (self.noise != "")
567 precache_sound(self.noise);
568 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
571 self.active = ACTIVE_ACTIVE;
573 // keys: angle, speed, phase, noise, freq
577 // not initializing self.dmg to 2, to allow damageless pendulum
579 if(self.dmg & (!self.message))
580 self.message = " was squished";
581 if(self.dmg && (!self.message2))
582 self.message2 = "was squished by";
583 if(self.dmg && (!self.dmgtime))
585 self.dmgtime2 = time;
587 self.blocked = generic_plat_blocked;
589 self.avelocity_z = 0.0000001;
590 if not(InitMovingBrushTrigger())
595 // find pendulum length (same formula as Q3A)
596 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
599 // copy initial angle
600 self.cnt = self.angles_z;
602 // wait for targets to spawn
603 controller = spawn();
604 controller.classname = "func_pendulum_controller";
605 controller.owner = self;
606 controller.nextthink = time + 1;
607 controller.think = func_pendulum_controller_think;
608 self.nextthink = self.ltime + 999999999;
609 self.think = SUB_Null;
611 //self.effects |= EF_LOWPRECISION;
613 // TODO make a reset function for this one
616 // button and multiple button
619 void() button_return;
623 self.state = STATE_TOP;
624 self.nextthink = self.ltime + self.wait;
625 self.think = button_return;
626 activator = self.enemy;
628 self.frame = 1; // use alternate textures
633 self.state = STATE_BOTTOM;
638 self.state = STATE_DOWN;
639 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
640 self.frame = 0; // use normal textures
642 self.takedamage = DAMAGE_YES; // can be shot again
646 void button_blocked()
648 // do nothing, just don't come all the way back out
654 self.health = self.max_health;
655 self.takedamage = DAMAGE_NO; // will be reset upon return
657 if (self.state == STATE_UP || self.state == STATE_TOP)
660 if (self.noise != "")
661 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
663 self.state = STATE_UP;
664 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
669 self.health = self.max_health;
670 setorigin(self, self.pos1);
671 self.frame = 0; // use normal textures
672 self.state = STATE_BOTTOM;
674 self.takedamage = DAMAGE_YES; // can be shot again
679 // if (activator.classname != "player")
681 // dprint(activator.classname);
682 // dprint(" triggered a button\n");
685 if not (self.active == ACTIVE_ACTIVE)
688 self.enemy = activator;
694 // if (activator.classname != "player")
696 // dprint(activator.classname);
697 // dprint(" touched a button\n");
701 if not(other.iscreature)
703 if(other.velocity * self.movedir < 0)
707 self.enemy = other.owner;
711 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
713 if(self.spawnflags & DOOR_NOSPLASH)
714 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
716 self.health = self.health - damage;
717 if (self.health <= 0)
719 // if (activator.classname != "player")
721 // dprint(activator.classname);
722 // dprint(" killed a button\n");
724 self.enemy = damage_attacker;
730 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
731 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.
733 "angle" determines the opening direction
734 "target" all entities with a matching targetname will be used
735 "speed" override the default 40 speed
736 "wait" override the default 1 second wait (-1 = never return)
737 "lip" override the default 4 pixel lip remaining at end of move
738 "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
745 void spawnfunc_func_button()
749 if not(InitMovingBrushTrigger())
751 self.effects |= EF_LOWPRECISION;
753 self.blocked = button_blocked;
754 self.use = button_use;
756 // if (self.health == 0) // all buttons are now shootable
760 self.max_health = self.health;
761 self.event_damage = button_damage;
762 self.takedamage = DAMAGE_YES;
765 self.touch = button_touch;
775 precache_sound(self.noise);
777 self.active = ACTIVE_ACTIVE;
779 self.pos1 = self.origin;
780 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
781 self.flags |= FL_NOTARGET;
787 float DOOR_START_OPEN = 1;
788 float DOOR_DONT_LINK = 4;
789 float DOOR_TOGGLE = 32;
793 Doors are similar to buttons, but can spawn a fat trigger field around them
794 to open without a touch, and they link together to form simultanious
797 Door.owner is the master door. If there is only one door, it points to itself.
798 If multiple doors, all will point to a single one.
800 Door.enemy chains from the master door through all doors linked in the chain.
805 =============================================================================
809 =============================================================================
814 void() door_rotating_go_down;
815 void() door_rotating_go_up;
820 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
821 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
824 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
825 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
827 //Dont chamge direction for dead or dying stuff
828 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
831 if (self.state == STATE_DOWN)
832 if (self.classname == "door")
837 door_rotating_go_up ();
840 if (self.classname == "door")
845 door_rotating_go_down ();
849 //gib dying stuff just to make sure
850 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
851 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
855 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
856 // if a door has a negative wait, it would never come back if blocked,
857 // so let it just squash the object to death real fast
858 /* if (self.wait >= 0)
860 if (self.state == STATE_DOWN)
871 if (self.noise1 != "")
872 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
873 self.state = STATE_TOP;
874 if (self.spawnflags & DOOR_TOGGLE)
875 return; // don't come down automatically
876 if (self.classname == "door")
878 self.think = door_go_down;
881 self.think = door_rotating_go_down;
883 self.nextthink = self.ltime + self.wait;
886 void door_hit_bottom()
888 if (self.noise1 != "")
889 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
890 self.state = STATE_BOTTOM;
895 if (self.noise2 != "")
896 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
899 self.takedamage = DAMAGE_YES;
900 self.health = self.max_health;
903 self.state = STATE_DOWN;
904 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
909 if (self.state == STATE_UP)
910 return; // already going up
912 if (self.state == STATE_TOP)
913 { // reset top wait time
914 self.nextthink = self.ltime + self.wait;
918 if (self.noise2 != "")
919 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
920 self.state = STATE_UP;
921 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
924 oldmessage = self.message;
927 self.message = oldmessage;
933 =============================================================================
937 =============================================================================
940 float door_check_keys(void) {
950 if not(door.itemkeys)
953 // this door require a key
954 // only a player can have a key
955 if (other.classname != "player")
958 if (item_keys_usekey(door, other)) {
959 // some keys were used
960 if (other.key_door_messagetime <= time) {
961 play2(other, "misc/talk.wav");
962 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
963 other.key_door_messagetime = time + 2;
967 if (other.key_door_messagetime <= time) {
968 play2(other, "misc/talk.wav");
969 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
970 other.key_door_messagetime = time + 2;
975 // door is now unlocked
976 play2(other, "misc/talk.wav");
977 centerprint(other, "Door unlocked!");
989 if (self.owner != self)
990 objerror ("door_fire: self.owner != self");
994 if (self.spawnflags & DOOR_TOGGLE)
996 if (self.state == STATE_UP || self.state == STATE_TOP)
1001 if (self.classname == "door")
1007 door_rotating_go_down ();
1010 } while ( (self != starte) && (self != world) );
1016 // trigger all paired doors
1020 if (self.classname == "door")
1025 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1026 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1028 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1029 self.pos2 = '0 0 0' - self.pos2;
1031 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1032 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1033 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1035 door_rotating_go_up ();
1039 } while ( (self != starte) && (self != world) );
1048 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1060 void door_trigger_touch()
1062 if (other.health < 1)
1063 if not(other.iscreature && other.deadflag == DEAD_NO)
1066 if (time < self.attack_finished_single)
1069 // check if door is locked
1070 if (!door_check_keys())
1073 self.attack_finished_single = time + 1;
1082 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1085 if(self.spawnflags & DOOR_NOSPLASH)
1086 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1088 self.health = self.health - damage;
1090 if (self.itemkeys) {
1091 // don't allow opening doors through damage if keys are required
1095 if (self.health <= 0)
1099 self.health = self.max_health;
1100 self.takedamage = DAMAGE_NO; // wil be reset upon return
1116 if(other.classname != "player")
1118 if (self.owner.attack_finished_single > time)
1121 self.owner.attack_finished_single = time + 2;
1123 if (!(self.owner.dmg) && (self.owner.message != ""))
1125 if (other.flags & FL_CLIENT)
1126 centerprint (other, self.owner.message);
1127 play2(other, "misc/talk.wav");
1132 void door_generic_plat_blocked()
1135 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1136 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1139 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1140 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1142 //Dont chamge direction for dead or dying stuff
1143 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1146 if (self.state == STATE_DOWN)
1147 door_rotating_go_up ();
1149 door_rotating_go_down ();
1152 //gib dying stuff just to make sure
1153 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1154 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1158 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1159 // if a door has a negative wait, it would never come back if blocked,
1160 // so let it just squash the object to death real fast
1161 /* if (self.wait >= 0)
1163 if (self.state == STATE_DOWN)
1164 door_rotating_go_up ();
1166 door_rotating_go_down ();
1172 void door_rotating_hit_top()
1174 if (self.noise1 != "")
1175 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1176 self.state = STATE_TOP;
1177 if (self.spawnflags & DOOR_TOGGLE)
1178 return; // don't come down automatically
1179 self.think = door_rotating_go_down;
1180 self.nextthink = self.ltime + self.wait;
1183 void door_rotating_hit_bottom()
1185 if (self.noise1 != "")
1186 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1187 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1189 self.pos2 = '0 0 0' - self.pos2;
1192 self.state = STATE_BOTTOM;
1195 void door_rotating_go_down()
1197 if (self.noise2 != "")
1198 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1199 if (self.max_health)
1201 self.takedamage = DAMAGE_YES;
1202 self.health = self.max_health;
1205 self.state = STATE_DOWN;
1206 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1209 void door_rotating_go_up()
1211 if (self.state == STATE_UP)
1212 return; // already going up
1214 if (self.state == STATE_TOP)
1215 { // reset top wait time
1216 self.nextthink = self.ltime + self.wait;
1219 if (self.noise2 != "")
1220 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1221 self.state = STATE_UP;
1222 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1225 oldmessage = self.message;
1228 self.message = oldmessage;
1235 =============================================================================
1239 =============================================================================
1243 entity spawn_field(vector fmins, vector fmaxs)
1249 trigger.classname = "doortriggerfield";
1250 trigger.movetype = MOVETYPE_NONE;
1251 trigger.solid = SOLID_TRIGGER;
1252 trigger.owner = self;
1253 trigger.touch = door_trigger_touch;
1257 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1262 float EntitiesTouching(entity e1, entity e2)
1264 if (e1.absmin_x > e2.absmax_x)
1266 if (e1.absmin_y > e2.absmax_y)
1268 if (e1.absmin_z > e2.absmax_z)
1270 if (e1.absmax_x < e2.absmin_x)
1272 if (e1.absmax_y < e2.absmin_y)
1274 if (e1.absmax_z < e2.absmin_z)
1290 vector cmins, cmaxs;
1293 return; // already linked by another door
1294 if (self.spawnflags & 4)
1296 self.owner = self.enemy = self;
1304 self.trigger_field = spawn_field(self.absmin, self.absmax);
1306 return; // don't want to link this door
1309 cmins = self.absmin;
1310 cmaxs = self.absmax;
1317 self.owner = starte; // master door
1320 starte.health = self.health;
1322 starte.targetname = self.targetname;
1323 if (self.message != "")
1324 starte.message = self.message;
1326 t = find(t, classname, self.classname);
1329 self.enemy = starte; // make the chain a loop
1331 // shootable, or triggered doors just needed the owner/enemy links,
1332 // they don't spawn a field
1343 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1348 if (EntitiesTouching(self,t))
1351 objerror ("cross connected doors");
1356 if (t.absmin_x < cmins_x)
1357 cmins_x = t.absmin_x;
1358 if (t.absmin_y < cmins_y)
1359 cmins_y = t.absmin_y;
1360 if (t.absmin_z < cmins_z)
1361 cmins_z = t.absmin_z;
1362 if (t.absmax_x > cmaxs_x)
1363 cmaxs_x = t.absmax_x;
1364 if (t.absmax_y > cmaxs_y)
1365 cmaxs_y = t.absmax_y;
1366 if (t.absmax_z > cmaxs_z)
1367 cmaxs_z = t.absmax_z;
1374 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1375 if two doors touch, they are assumed to be connected and operate as a unit.
1377 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1379 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).
1381 GOLD_KEY causes the door to open only if the activator holds a gold key.
1383 SILVER_KEY causes the door to open only if the activator holds a silver key.
1385 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1386 "angle" determines the opening direction
1387 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1388 "health" if set, door must be shot open
1389 "speed" movement speed (100 default)
1390 "wait" wait before returning (3 default, -1 = never return)
1391 "lip" lip remaining at end of move (8 default)
1392 "dmg" damage to inflict when blocked (2 default)
1399 FIXME: only one sound set available at the time being
1403 void door_init_startopen()
1405 setorigin (self, self.pos2);
1406 self.pos2 = self.pos1;
1407 self.pos1 = self.origin;
1412 setorigin(self, self.pos1);
1413 self.velocity = '0 0 0';
1414 self.state = STATE_BOTTOM;
1415 self.think = SUB_Null;
1418 // spawnflags require key (for now only func_door)
1419 #define SPAWNFLAGS_GOLD_KEY 8
1420 #define SPAWNFLAGS_SILVER_KEY 16
1421 void spawnfunc_func_door()
1423 // Quake 1 keys compatibility
1424 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1425 self.itemkeys |= ITEM_KEY_BIT(0);
1426 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1427 self.itemkeys |= ITEM_KEY_BIT(1);
1429 //if (!self.deathtype) // map makers can override this
1430 // self.deathtype = " got in the way";
1433 self.max_health = self.health;
1434 if not(InitMovingBrushTrigger())
1436 self.effects |= EF_LOWPRECISION;
1437 self.classname = "door";
1439 self.blocked = door_blocked;
1440 self.use = door_use;
1442 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1443 // if(self.spawnflags & 8)
1444 // self.dmg = 10000;
1446 if(self.dmg && (!self.message))
1447 self.message = "was squished";
1448 if(self.dmg && (!self.message2))
1449 self.message2 = "was squished by";
1451 if (self.sounds > 0)
1453 precache_sound ("plats/medplat1.wav");
1454 precache_sound ("plats/medplat2.wav");
1455 self.noise2 = "plats/medplat1.wav";
1456 self.noise1 = "plats/medplat2.wav";
1466 self.pos1 = self.origin;
1467 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1469 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1470 // but spawn in the open position
1471 if (self.spawnflags & DOOR_START_OPEN)
1472 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1474 self.state = STATE_BOTTOM;
1478 self.takedamage = DAMAGE_YES;
1479 self.event_damage = door_damage;
1485 self.touch = door_touch;
1487 // LinkDoors can't be done until all of the doors have been spawned, so
1488 // the sizes can be detected properly.
1489 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1491 self.reset = door_reset;
1494 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1495 if two doors touch, they are assumed to be connected and operate as a unit.
1497 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1499 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1500 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1501 must have set trigger_reverse to 1.
1502 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1504 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).
1506 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1507 "angle" determines the destination angle for opening. negative values reverse the direction.
1508 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1509 "health" if set, door must be shot open
1510 "speed" movement speed (100 default)
1511 "wait" wait before returning (3 default, -1 = never return)
1512 "dmg" damage to inflict when blocked (2 default)
1519 FIXME: only one sound set available at the time being
1522 void door_rotating_reset()
1524 self.angles = self.pos1;
1525 self.avelocity = '0 0 0';
1526 self.state = STATE_BOTTOM;
1527 self.think = SUB_Null;
1530 void door_rotating_init_startopen()
1532 self.angles = self.movedir;
1533 self.pos2 = '0 0 0';
1534 self.pos1 = self.movedir;
1538 void spawnfunc_func_door_rotating()
1541 //if (!self.deathtype) // map makers can override this
1542 // self.deathtype = " got in the way";
1544 // I abuse "movedir" for denoting the axis for now
1545 if (self.spawnflags & 64) // X (untested)
1546 self.movedir = '0 0 1';
1547 else if (self.spawnflags & 128) // Y (untested)
1548 self.movedir = '1 0 0';
1550 self.movedir = '0 1 0';
1552 if (self.angles_y==0) self.angles_y = 90;
1554 self.movedir = self.movedir * self.angles_y;
1555 self.angles = '0 0 0';
1557 self.max_health = self.health;
1558 self.avelocity = self.movedir;
1559 if not(InitMovingBrushTrigger())
1561 self.velocity = '0 0 0';
1562 //self.effects |= EF_LOWPRECISION;
1563 self.classname = "door_rotating";
1565 self.blocked = door_blocked;
1566 self.use = door_use;
1568 if(self.spawnflags & 8)
1571 if(self.dmg && (!self.message))
1572 self.message = "was squished";
1573 if(self.dmg && (!self.message2))
1574 self.message2 = "was squished by";
1576 if (self.sounds > 0)
1578 precache_sound ("plats/medplat1.wav");
1579 precache_sound ("plats/medplat2.wav");
1580 self.noise2 = "plats/medplat1.wav";
1581 self.noise1 = "plats/medplat2.wav";
1588 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1590 self.pos1 = '0 0 0';
1591 self.pos2 = self.movedir;
1593 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1594 // but spawn in the open position
1595 if (self.spawnflags & DOOR_START_OPEN)
1596 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1598 self.state = STATE_BOTTOM;
1602 self.takedamage = DAMAGE_YES;
1603 self.event_damage = door_damage;
1609 self.touch = door_touch;
1611 // LinkDoors can't be done until all of the doors have been spawned, so
1612 // the sizes can be detected properly.
1613 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1615 self.reset = door_rotating_reset;
1619 =============================================================================
1623 =============================================================================
1626 void() fd_secret_move1;
1627 void() fd_secret_move2;
1628 void() fd_secret_move3;
1629 void() fd_secret_move4;
1630 void() fd_secret_move5;
1631 void() fd_secret_move6;
1632 void() fd_secret_done;
1634 float SECRET_OPEN_ONCE = 1; // stays open
1635 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1636 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1637 float SECRET_NO_SHOOT = 8; // only opened by trigger
1638 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1641 void fd_secret_use()
1644 string message_save;
1646 self.health = 10000;
1647 self.bot_attack = TRUE;
1649 // exit if still moving around...
1650 if (self.origin != self.oldorigin)
1653 message_save = self.message;
1654 self.message = ""; // no more message
1655 SUB_UseTargets(); // fire all targets / killtargets
1656 self.message = message_save;
1658 self.velocity = '0 0 0';
1660 // Make a sound, wait a little...
1662 if (self.noise1 != "")
1663 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1664 self.nextthink = self.ltime + 0.1;
1666 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1667 makevectors(self.mangle);
1671 if (self.spawnflags & SECRET_1ST_DOWN)
1672 self.t_width = fabs(v_up * self.size);
1674 self.t_width = fabs(v_right * self.size);
1678 self.t_length = fabs(v_forward * self.size);
1680 if (self.spawnflags & SECRET_1ST_DOWN)
1681 self.dest1 = self.origin - v_up * self.t_width;
1683 self.dest1 = self.origin + v_right * (self.t_width * temp);
1685 self.dest2 = self.dest1 + v_forward * self.t_length;
1686 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1687 if (self.noise2 != "")
1688 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1691 // Wait after first movement...
1692 void fd_secret_move1()
1694 self.nextthink = self.ltime + 1.0;
1695 self.think = fd_secret_move2;
1696 if (self.noise3 != "")
1697 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1700 // Start moving sideways w/sound...
1701 void fd_secret_move2()
1703 if (self.noise2 != "")
1704 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1705 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1708 // Wait here until time to go back...
1709 void fd_secret_move3()
1711 if (self.noise3 != "")
1712 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1713 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1715 self.nextthink = self.ltime + self.wait;
1716 self.think = fd_secret_move4;
1721 void fd_secret_move4()
1723 if (self.noise2 != "")
1724 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1725 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1729 void fd_secret_move5()
1731 self.nextthink = self.ltime + 1.0;
1732 self.think = fd_secret_move6;
1733 if (self.noise3 != "")
1734 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1737 void fd_secret_move6()
1739 if (self.noise2 != "")
1740 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1741 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1744 void fd_secret_done()
1746 if (self.spawnflags&SECRET_YES_SHOOT)
1748 self.health = 10000;
1749 self.takedamage = DAMAGE_YES;
1750 //self.th_pain = fd_secret_use;
1752 if (self.noise3 != "")
1753 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1756 void secret_blocked()
1758 if (time < self.attack_finished_single)
1760 self.attack_finished_single = time + 0.5;
1761 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1773 if not(other.iscreature)
1775 if (self.attack_finished_single > time)
1778 self.attack_finished_single = time + 2;
1782 if (other.flags & FL_CLIENT)
1783 centerprint (other, self.message);
1784 play2(other, "misc/talk.wav");
1790 if (self.spawnflags&SECRET_YES_SHOOT)
1792 self.health = 10000;
1793 self.takedamage = DAMAGE_YES;
1795 setorigin(self, self.oldorigin);
1796 self.think = SUB_Null;
1799 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1800 Basic secret door. Slides back, then to the side. Angle determines direction.
1801 wait = # of seconds before coming back
1802 1st_left = 1st move is left of arrow
1803 1st_down = 1st move is down from arrow
1804 always_shoot = even if targeted, keep shootable
1805 t_width = override WIDTH to move back (or height if going down)
1806 t_length = override LENGTH to move sideways
1807 "dmg" damage to inflict when blocked (2 default)
1809 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1816 void spawnfunc_func_door_secret()
1818 /*if (!self.deathtype) // map makers can override this
1819 self.deathtype = " got in the way";*/
1825 self.mangle = self.angles;
1826 self.angles = '0 0 0';
1827 self.classname = "door";
1828 if not(InitMovingBrushTrigger())
1830 self.effects |= EF_LOWPRECISION;
1832 self.touch = secret_touch;
1833 self.blocked = secret_blocked;
1835 self.use = fd_secret_use;
1840 self.spawnflags |= SECRET_YES_SHOOT;
1842 if(self.spawnflags&SECRET_YES_SHOOT)
1844 self.health = 10000;
1845 self.takedamage = DAMAGE_YES;
1846 self.event_damage = fd_secret_use;
1848 self.oldorigin = self.origin;
1850 self.wait = 5; // 5 seconds before closing
1852 self.reset = secret_reset;
1856 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1857 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1858 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
1859 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1860 height: amplitude modifier (default 32)
1861 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1862 noise: path/name of looping .wav file to play.
1863 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1867 void func_fourier_controller_think()
1872 self.nextthink = time + 0.1;
1873 if not (self.owner.active == ACTIVE_ACTIVE)
1875 self.owner.velocity = '0 0 0';
1880 n = floor((tokenize_console(self.owner.netname)) / 5);
1881 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1883 v = self.owner.destvec;
1885 for(i = 0; i < n; ++i)
1887 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1888 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;
1891 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1892 // * 10 so it will arrive in 0.1 sec
1893 self.owner.velocity = (v - self.owner.origin) * 10;
1896 void spawnfunc_func_fourier()
1899 if (self.noise != "")
1901 precache_sound(self.noise);
1902 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1909 self.destvec = self.origin;
1910 self.cnt = 360 / self.speed;
1912 self.blocked = generic_plat_blocked;
1913 if(self.dmg & (!self.message))
1914 self.message = " was squished";
1915 if(self.dmg && (!self.message2))
1916 self.message2 = "was squished by";
1917 if(self.dmg && (!self.dmgtime))
1918 self.dmgtime = 0.25;
1919 self.dmgtime2 = time;
1921 if(self.netname == "")
1922 self.netname = "1 0 0 0 1";
1924 if not(InitMovingBrushTrigger())
1927 self.active = ACTIVE_ACTIVE;
1929 // wait for targets to spawn
1930 controller = spawn();
1931 controller.classname = "func_fourier_controller";
1932 controller.owner = self;
1933 controller.nextthink = time + 1;
1934 controller.think = func_fourier_controller_think;
1935 self.nextthink = self.ltime + 999999999;
1936 self.think = SUB_Null;
1938 // Savage: Reduce bandwith, critical on e.g. nexdm02
1939 self.effects |= EF_LOWPRECISION;
1941 // TODO make a reset function for this one
1944 // reusing some fields havocbots declared
1945 .entity wp00, wp01, wp02, wp03;
1947 .float targetfactor, target2factor, target3factor, target4factor;
1948 .vector targetnormal, target2normal, target3normal, target4normal;
1950 vector func_vectormamamam_origin(entity o, float t)
1962 p = e.origin + t * e.velocity;
1964 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1966 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1972 p = e.origin + t * e.velocity;
1974 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1976 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1982 p = e.origin + t * e.velocity;
1984 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1986 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1992 p = e.origin + t * e.velocity;
1994 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1996 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2002 void func_vectormamamam_controller_think()
2004 self.nextthink = time + 0.1;
2006 if not (self.owner.active == ACTIVE_ACTIVE)
2008 self.owner.velocity = '0 0 0';
2012 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2013 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2016 void func_vectormamamam_findtarget()
2018 if(self.target != "")
2019 self.wp00 = find(world, targetname, self.target);
2021 if(self.target2 != "")
2022 self.wp01 = find(world, targetname, self.target2);
2024 if(self.target3 != "")
2025 self.wp02 = find(world, targetname, self.target3);
2027 if(self.target4 != "")
2028 self.wp03 = find(world, targetname, self.target4);
2030 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2031 objerror("No reference entity found, so there is nothing to move. Aborting.");
2033 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2036 controller = spawn();
2037 controller.classname = "func_vectormamamam_controller";
2038 controller.owner = self;
2039 controller.nextthink = time + 1;
2040 controller.think = func_vectormamamam_controller_think;
2043 void spawnfunc_func_vectormamamam()
2045 if (self.noise != "")
2047 precache_sound(self.noise);
2048 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2051 if(!self.targetfactor)
2052 self.targetfactor = 1;
2054 if(!self.target2factor)
2055 self.target2factor = 1;
2057 if(!self.target3factor)
2058 self.target3factor = 1;
2060 if(!self.target4factor)
2061 self.target4factor = 1;
2063 if(vlen(self.targetnormal))
2064 self.targetnormal = normalize(self.targetnormal);
2066 if(vlen(self.target2normal))
2067 self.target2normal = normalize(self.target2normal);
2069 if(vlen(self.target3normal))
2070 self.target3normal = normalize(self.target3normal);
2072 if(vlen(self.target4normal))
2073 self.target4normal = normalize(self.target4normal);
2075 self.blocked = generic_plat_blocked;
2076 if(self.dmg & (!self.message))
2077 self.message = " was squished";
2078 if(self.dmg && (!self.message2))
2079 self.message2 = "was squished by";
2080 if(self.dmg && (!self.dmgtime))
2081 self.dmgtime = 0.25;
2082 self.dmgtime2 = time;
2084 if(self.netname == "")
2085 self.netname = "1 0 0 0 1";
2087 if not(InitMovingBrushTrigger())
2090 // wait for targets to spawn
2091 self.nextthink = self.ltime + 999999999;
2092 self.think = SUB_Null;
2094 // Savage: Reduce bandwith, critical on e.g. nexdm02
2095 self.effects |= EF_LOWPRECISION;
2097 self.active = ACTIVE_ACTIVE;
2099 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2102 void conveyor_think()
2106 // set myself as current conveyor where possible
2107 for(e = world; (e = findentity(e, conveyor, self)); )
2112 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2113 if(!e.conveyor.state)
2116 vector emin = e.absmin;
2117 vector emax = e.absmax;
2118 if(self.solid == SOLID_BSP)
2123 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2124 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2128 for(e = world; (e = findentity(e, conveyor, self)); )
2130 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2131 continue; // done in SV_PlayerPhysics
2133 setorigin(e, e.origin + self.movedir * sys_frametime);
2134 move_out_of_solid(e);
2135 UpdateCSQCProjectile(e);
2137 // stupid conveyor code
2138 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2139 if(trace_fraction > 0)
2140 setorigin(e, trace_endpos);
2145 self.nextthink = time;
2150 self.state = !self.state;
2153 void conveyor_reset()
2155 self.state = (self.spawnflags & 1);
2158 void conveyor_init()
2162 self.movedir = self.movedir * self.speed;
2163 self.think = conveyor_think;
2164 self.nextthink = time;
2167 self.use = conveyor_use;
2168 self.reset = conveyor_reset;
2175 void spawnfunc_trigger_conveyor()
2182 void spawnfunc_func_conveyor()
2185 InitMovingBrushTrigger();
2186 self.movetype = MOVETYPE_NONE;