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;
265 // if using bezier curves and turning is enabled, the train will turn toward the next point while waiting
266 if(!self.train_wait_turning)
267 if(self.spawnflags & 1 && self.bezier_turn && self.wait >= 0)
271 targ = find(world, targetname, self.target);
272 org = vectoangles(targ.origin);
273 SUB_CalcAngleMove(org, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
274 self.train_wait_turning = TRUE;
279 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
281 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
283 self.train_wait_turning = FALSE;
288 self.think = train_next;
289 self.nextthink = self.ltime + self.wait;
298 targ = find(world, targetname, self.target);
299 self.target = targ.target;
300 if (self.spawnflags & 1)
302 cp = find(world, target, targ.targetname); // get the previous corner first
303 cp = find(world, targetname, cp.curve); // now get its second target (the control point)
304 if(cp.targetname == "")
305 cp_org = targ.origin - self.mins; // no control point found, assume a straight line to the destination
307 cp_org = cp.origin - self.mins;
310 objerror("train_next: no next target");
311 self.wait = targ.wait;
317 if (self.spawnflags & 1)
318 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
320 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
324 if (self.spawnflags & 1)
325 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
327 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
331 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
334 void func_train_find()
337 targ = find(world, targetname, self.target);
338 self.target = targ.target;
340 objerror("func_train_find: no next target");
341 setorigin(self, targ.origin - self.mins);
342 self.nextthink = self.ltime + 1;
343 self.think = train_next;
346 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
347 Ridable platform, targets spawnfunc_path_corner path to follow.
348 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
349 target : targetname of first spawnfunc_path_corner (starts here)
351 void spawnfunc_func_train()
353 if (self.noise != "")
354 precache_sound(self.noise);
357 objerror("func_train without a target");
360 if (self.spawnflags & 2)
361 self.bezier_turn = TRUE;
363 if not(InitMovingBrushTrigger())
365 self.effects |= EF_LOWPRECISION;
367 // wait for targets to spawn
368 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
370 self.blocked = generic_plat_blocked;
371 if(self.dmg & (!self.message))
372 self.message = " was squished";
373 if(self.dmg && (!self.message2))
374 self.message2 = "was squished by";
375 if(self.dmg && (!self.dmgtime))
377 self.dmgtime2 = time;
379 // TODO make a reset function for this one
382 void func_rotating_setactive(float astate)
385 if (astate == ACTIVE_TOGGLE)
387 if(self.active == ACTIVE_ACTIVE)
388 self.active = ACTIVE_NOT;
390 self.active = ACTIVE_ACTIVE;
393 self.active = astate;
395 if(self.active == ACTIVE_NOT)
396 self.avelocity = '0 0 0';
398 self.avelocity = self.pos1;
401 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
402 Brush model that spins in place on one axis (default Z).
403 speed : speed to rotate (in degrees per second)
404 noise : path/name of looping .wav file to play.
405 dmg : Do this mutch dmg every .dmgtime intervall when blocked
409 void spawnfunc_func_rotating()
411 if (self.noise != "")
413 precache_sound(self.noise);
414 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
417 self.active = ACTIVE_ACTIVE;
418 self.setactive = func_rotating_setactive;
422 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
423 if (self.spawnflags & 4) // X (untested)
424 self.avelocity = '0 0 1' * self.speed;
425 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
426 else if (self.spawnflags & 8) // Y (untested)
427 self.avelocity = '1 0 0' * self.speed;
428 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
430 self.avelocity = '0 1 0' * self.speed;
432 self.pos1 = self.avelocity;
434 if(self.dmg & (!self.message))
435 self.message = " was squished";
436 if(self.dmg && (!self.message2))
437 self.message2 = "was squished by";
440 if(self.dmg && (!self.dmgtime))
443 self.dmgtime2 = time;
445 if not(InitMovingBrushTrigger())
447 // no EF_LOWPRECISION here, as rounding angles is bad
449 self.blocked = generic_plat_blocked;
451 // wait for targets to spawn
452 self.nextthink = self.ltime + 999999999;
453 self.think = SUB_Null;
455 // TODO make a reset function for this one
459 void func_bobbing_controller_think()
462 self.nextthink = time + 0.1;
464 if not (self.owner.active == ACTIVE_ACTIVE)
466 self.owner.velocity = '0 0 0';
470 // calculate sinewave using makevectors
471 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
472 v = self.owner.destvec + self.owner.movedir * v_forward_y;
473 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
474 // * 10 so it will arrive in 0.1 sec
475 self.owner.velocity = (v - self.owner.origin) * 10;
478 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
479 Brush model that moves back and forth on one axis (default Z).
480 speed : how long one cycle takes in seconds (default 4)
481 height : how far the cycle moves (default 32)
482 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
483 noise : path/name of looping .wav file to play.
484 dmg : Do this mutch dmg every .dmgtime intervall when blocked
487 void spawnfunc_func_bobbing()
490 if (self.noise != "")
492 precache_sound(self.noise);
493 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
499 // center of bobbing motion
500 self.destvec = self.origin;
501 // time scale to get degrees
502 self.cnt = 360 / self.speed;
504 self.active = ACTIVE_ACTIVE;
506 // damage when blocked
507 self.blocked = generic_plat_blocked;
508 if(self.dmg & (!self.message))
509 self.message = " was squished";
510 if(self.dmg && (!self.message2))
511 self.message2 = "was squished by";
512 if(self.dmg && (!self.dmgtime))
514 self.dmgtime2 = time;
517 if (self.spawnflags & 1) // X
518 self.movedir = '1 0 0' * self.height;
519 else if (self.spawnflags & 2) // Y
520 self.movedir = '0 1 0' * self.height;
522 self.movedir = '0 0 1' * self.height;
524 if not(InitMovingBrushTrigger())
527 // wait for targets to spawn
528 controller = spawn();
529 controller.classname = "func_bobbing_controller";
530 controller.owner = self;
531 controller.nextthink = time + 1;
532 controller.think = func_bobbing_controller_think;
533 self.nextthink = self.ltime + 999999999;
534 self.think = SUB_Null;
536 // Savage: Reduce bandwith, critical on e.g. nexdm02
537 self.effects |= EF_LOWPRECISION;
539 // TODO make a reset function for this one
543 void func_pendulum_controller_think()
546 self.nextthink = time + 0.1;
548 if not (self.owner.active == ACTIVE_ACTIVE)
550 self.owner.avelocity_x = 0;
554 // calculate sinewave using makevectors
555 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
556 v = self.owner.speed * v_forward_y + self.cnt;
557 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
559 // * 10 so it will arrive in 0.1 sec
560 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
564 void spawnfunc_func_pendulum()
567 if (self.noise != "")
569 precache_sound(self.noise);
570 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
573 self.active = ACTIVE_ACTIVE;
575 // keys: angle, speed, phase, noise, freq
579 // not initializing self.dmg to 2, to allow damageless pendulum
581 if(self.dmg & (!self.message))
582 self.message = " was squished";
583 if(self.dmg && (!self.message2))
584 self.message2 = "was squished by";
585 if(self.dmg && (!self.dmgtime))
587 self.dmgtime2 = time;
589 self.blocked = generic_plat_blocked;
591 self.avelocity_z = 0.0000001;
592 if not(InitMovingBrushTrigger())
597 // find pendulum length (same formula as Q3A)
598 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
601 // copy initial angle
602 self.cnt = self.angles_z;
604 // wait for targets to spawn
605 controller = spawn();
606 controller.classname = "func_pendulum_controller";
607 controller.owner = self;
608 controller.nextthink = time + 1;
609 controller.think = func_pendulum_controller_think;
610 self.nextthink = self.ltime + 999999999;
611 self.think = SUB_Null;
613 //self.effects |= EF_LOWPRECISION;
615 // TODO make a reset function for this one
618 // button and multiple button
621 void() button_return;
625 self.state = STATE_TOP;
626 self.nextthink = self.ltime + self.wait;
627 self.think = button_return;
628 activator = self.enemy;
630 self.frame = 1; // use alternate textures
635 self.state = STATE_BOTTOM;
640 self.state = STATE_DOWN;
641 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
642 self.frame = 0; // use normal textures
644 self.takedamage = DAMAGE_YES; // can be shot again
648 void button_blocked()
650 // do nothing, just don't come all the way back out
656 self.health = self.max_health;
657 self.takedamage = DAMAGE_NO; // will be reset upon return
659 if (self.state == STATE_UP || self.state == STATE_TOP)
662 if (self.noise != "")
663 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
665 self.state = STATE_UP;
666 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
671 self.health = self.max_health;
672 setorigin(self, self.pos1);
673 self.frame = 0; // use normal textures
674 self.state = STATE_BOTTOM;
676 self.takedamage = DAMAGE_YES; // can be shot again
681 // if (activator.classname != "player")
683 // dprint(activator.classname);
684 // dprint(" triggered a button\n");
687 if not (self.active == ACTIVE_ACTIVE)
690 self.enemy = activator;
696 // if (activator.classname != "player")
698 // dprint(activator.classname);
699 // dprint(" touched a button\n");
703 if not(other.iscreature)
705 if(other.velocity * self.movedir < 0)
709 self.enemy = other.owner;
713 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
715 if(self.spawnflags & DOOR_NOSPLASH)
716 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
718 self.health = self.health - damage;
719 if (self.health <= 0)
721 // if (activator.classname != "player")
723 // dprint(activator.classname);
724 // dprint(" killed a button\n");
726 self.enemy = damage_attacker;
732 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
733 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.
735 "angle" determines the opening direction
736 "target" all entities with a matching targetname will be used
737 "speed" override the default 40 speed
738 "wait" override the default 1 second wait (-1 = never return)
739 "lip" override the default 4 pixel lip remaining at end of move
740 "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
747 void spawnfunc_func_button()
751 if not(InitMovingBrushTrigger())
753 self.effects |= EF_LOWPRECISION;
755 self.blocked = button_blocked;
756 self.use = button_use;
758 // if (self.health == 0) // all buttons are now shootable
762 self.max_health = self.health;
763 self.event_damage = button_damage;
764 self.takedamage = DAMAGE_YES;
767 self.touch = button_touch;
777 precache_sound(self.noise);
779 self.active = ACTIVE_ACTIVE;
781 self.pos1 = self.origin;
782 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
783 self.flags |= FL_NOTARGET;
789 float DOOR_START_OPEN = 1;
790 float DOOR_DONT_LINK = 4;
791 float DOOR_TOGGLE = 32;
795 Doors are similar to buttons, but can spawn a fat trigger field around them
796 to open without a touch, and they link together to form simultanious
799 Door.owner is the master door. If there is only one door, it points to itself.
800 If multiple doors, all will point to a single one.
802 Door.enemy chains from the master door through all doors linked in the chain.
807 =============================================================================
811 =============================================================================
816 void() door_rotating_go_down;
817 void() door_rotating_go_up;
822 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
823 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
826 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
827 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
829 //Dont chamge direction for dead or dying stuff
830 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
833 if (self.state == STATE_DOWN)
834 if (self.classname == "door")
839 door_rotating_go_up ();
842 if (self.classname == "door")
847 door_rotating_go_down ();
851 //gib dying stuff just to make sure
852 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
853 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
857 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
858 // if a door has a negative wait, it would never come back if blocked,
859 // so let it just squash the object to death real fast
860 /* if (self.wait >= 0)
862 if (self.state == STATE_DOWN)
873 if (self.noise1 != "")
874 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
875 self.state = STATE_TOP;
876 if (self.spawnflags & DOOR_TOGGLE)
877 return; // don't come down automatically
878 if (self.classname == "door")
880 self.think = door_go_down;
883 self.think = door_rotating_go_down;
885 self.nextthink = self.ltime + self.wait;
888 void door_hit_bottom()
890 if (self.noise1 != "")
891 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
892 self.state = STATE_BOTTOM;
897 if (self.noise2 != "")
898 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
901 self.takedamage = DAMAGE_YES;
902 self.health = self.max_health;
905 self.state = STATE_DOWN;
906 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
911 if (self.state == STATE_UP)
912 return; // already going up
914 if (self.state == STATE_TOP)
915 { // reset top wait time
916 self.nextthink = self.ltime + self.wait;
920 if (self.noise2 != "")
921 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
922 self.state = STATE_UP;
923 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
926 oldmessage = self.message;
929 self.message = oldmessage;
935 =============================================================================
939 =============================================================================
942 float door_check_keys(void) {
952 if not(door.itemkeys)
955 // this door require a key
956 // only a player can have a key
957 if (other.classname != "player")
960 if (item_keys_usekey(door, other)) {
961 // some keys were used
962 if (other.key_door_messagetime <= time) {
963 play2(other, "misc/talk.wav");
964 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
965 other.key_door_messagetime = time + 2;
969 if (other.key_door_messagetime <= time) {
970 play2(other, "misc/talk.wav");
971 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
972 other.key_door_messagetime = time + 2;
977 // door is now unlocked
978 play2(other, "misc/talk.wav");
979 centerprint(other, "Door unlocked!");
991 if (self.owner != self)
992 objerror ("door_fire: self.owner != self");
996 if (self.spawnflags & DOOR_TOGGLE)
998 if (self.state == STATE_UP || self.state == STATE_TOP)
1003 if (self.classname == "door")
1009 door_rotating_go_down ();
1012 } while ( (self != starte) && (self != world) );
1018 // trigger all paired doors
1022 if (self.classname == "door")
1027 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1028 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1030 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1031 self.pos2 = '0 0 0' - self.pos2;
1033 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1034 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1035 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1037 door_rotating_go_up ();
1041 } while ( (self != starte) && (self != world) );
1050 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1062 void door_trigger_touch()
1064 if (other.health < 1)
1065 if not(other.iscreature && other.deadflag == DEAD_NO)
1068 if (time < self.attack_finished_single)
1071 // check if door is locked
1072 if (!door_check_keys())
1075 self.attack_finished_single = time + 1;
1084 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1087 if(self.spawnflags & DOOR_NOSPLASH)
1088 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1090 self.health = self.health - damage;
1092 if (self.itemkeys) {
1093 // don't allow opening doors through damage if keys are required
1097 if (self.health <= 0)
1101 self.health = self.max_health;
1102 self.takedamage = DAMAGE_NO; // wil be reset upon return
1118 if(other.classname != "player")
1120 if (self.owner.attack_finished_single > time)
1123 self.owner.attack_finished_single = time + 2;
1125 if (!(self.owner.dmg) && (self.owner.message != ""))
1127 if (other.flags & FL_CLIENT)
1128 centerprint (other, self.owner.message);
1129 play2(other, "misc/talk.wav");
1134 void door_generic_plat_blocked()
1137 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1138 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1141 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1142 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1144 //Dont chamge direction for dead or dying stuff
1145 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1148 if (self.state == STATE_DOWN)
1149 door_rotating_go_up ();
1151 door_rotating_go_down ();
1154 //gib dying stuff just to make sure
1155 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1156 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1160 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1161 // if a door has a negative wait, it would never come back if blocked,
1162 // so let it just squash the object to death real fast
1163 /* if (self.wait >= 0)
1165 if (self.state == STATE_DOWN)
1166 door_rotating_go_up ();
1168 door_rotating_go_down ();
1174 void door_rotating_hit_top()
1176 if (self.noise1 != "")
1177 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1178 self.state = STATE_TOP;
1179 if (self.spawnflags & DOOR_TOGGLE)
1180 return; // don't come down automatically
1181 self.think = door_rotating_go_down;
1182 self.nextthink = self.ltime + self.wait;
1185 void door_rotating_hit_bottom()
1187 if (self.noise1 != "")
1188 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1189 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1191 self.pos2 = '0 0 0' - self.pos2;
1194 self.state = STATE_BOTTOM;
1197 void door_rotating_go_down()
1199 if (self.noise2 != "")
1200 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1201 if (self.max_health)
1203 self.takedamage = DAMAGE_YES;
1204 self.health = self.max_health;
1207 self.state = STATE_DOWN;
1208 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1211 void door_rotating_go_up()
1213 if (self.state == STATE_UP)
1214 return; // already going up
1216 if (self.state == STATE_TOP)
1217 { // reset top wait time
1218 self.nextthink = self.ltime + self.wait;
1221 if (self.noise2 != "")
1222 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1223 self.state = STATE_UP;
1224 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1227 oldmessage = self.message;
1230 self.message = oldmessage;
1237 =============================================================================
1241 =============================================================================
1245 entity spawn_field(vector fmins, vector fmaxs)
1251 trigger.classname = "doortriggerfield";
1252 trigger.movetype = MOVETYPE_NONE;
1253 trigger.solid = SOLID_TRIGGER;
1254 trigger.owner = self;
1255 trigger.touch = door_trigger_touch;
1259 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1264 float EntitiesTouching(entity e1, entity e2)
1266 if (e1.absmin_x > e2.absmax_x)
1268 if (e1.absmin_y > e2.absmax_y)
1270 if (e1.absmin_z > e2.absmax_z)
1272 if (e1.absmax_x < e2.absmin_x)
1274 if (e1.absmax_y < e2.absmin_y)
1276 if (e1.absmax_z < e2.absmin_z)
1292 vector cmins, cmaxs;
1295 return; // already linked by another door
1296 if (self.spawnflags & 4)
1298 self.owner = self.enemy = self;
1306 self.trigger_field = spawn_field(self.absmin, self.absmax);
1308 return; // don't want to link this door
1311 cmins = self.absmin;
1312 cmaxs = self.absmax;
1319 self.owner = starte; // master door
1322 starte.health = self.health;
1324 starte.targetname = self.targetname;
1325 if (self.message != "")
1326 starte.message = self.message;
1328 t = find(t, classname, self.classname);
1331 self.enemy = starte; // make the chain a loop
1333 // shootable, or triggered doors just needed the owner/enemy links,
1334 // they don't spawn a field
1345 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1350 if (EntitiesTouching(self,t))
1353 objerror ("cross connected doors");
1358 if (t.absmin_x < cmins_x)
1359 cmins_x = t.absmin_x;
1360 if (t.absmin_y < cmins_y)
1361 cmins_y = t.absmin_y;
1362 if (t.absmin_z < cmins_z)
1363 cmins_z = t.absmin_z;
1364 if (t.absmax_x > cmaxs_x)
1365 cmaxs_x = t.absmax_x;
1366 if (t.absmax_y > cmaxs_y)
1367 cmaxs_y = t.absmax_y;
1368 if (t.absmax_z > cmaxs_z)
1369 cmaxs_z = t.absmax_z;
1376 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1377 if two doors touch, they are assumed to be connected and operate as a unit.
1379 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1381 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).
1383 GOLD_KEY causes the door to open only if the activator holds a gold key.
1385 SILVER_KEY causes the door to open only if the activator holds a silver key.
1387 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1388 "angle" determines the opening direction
1389 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1390 "health" if set, door must be shot open
1391 "speed" movement speed (100 default)
1392 "wait" wait before returning (3 default, -1 = never return)
1393 "lip" lip remaining at end of move (8 default)
1394 "dmg" damage to inflict when blocked (2 default)
1401 FIXME: only one sound set available at the time being
1405 void door_init_startopen()
1407 setorigin (self, self.pos2);
1408 self.pos2 = self.pos1;
1409 self.pos1 = self.origin;
1414 setorigin(self, self.pos1);
1415 self.velocity = '0 0 0';
1416 self.state = STATE_BOTTOM;
1417 self.think = SUB_Null;
1420 // spawnflags require key (for now only func_door)
1421 #define SPAWNFLAGS_GOLD_KEY 8
1422 #define SPAWNFLAGS_SILVER_KEY 16
1423 void spawnfunc_func_door()
1425 // Quake 1 keys compatibility
1426 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1427 self.itemkeys |= ITEM_KEY_BIT(0);
1428 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1429 self.itemkeys |= ITEM_KEY_BIT(1);
1431 //if (!self.deathtype) // map makers can override this
1432 // self.deathtype = " got in the way";
1435 self.max_health = self.health;
1436 if not(InitMovingBrushTrigger())
1438 self.effects |= EF_LOWPRECISION;
1439 self.classname = "door";
1441 self.blocked = door_blocked;
1442 self.use = door_use;
1444 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1445 // if(self.spawnflags & 8)
1446 // self.dmg = 10000;
1448 if(self.dmg && (!self.message))
1449 self.message = "was squished";
1450 if(self.dmg && (!self.message2))
1451 self.message2 = "was squished by";
1453 if (self.sounds > 0)
1455 precache_sound ("plats/medplat1.wav");
1456 precache_sound ("plats/medplat2.wav");
1457 self.noise2 = "plats/medplat1.wav";
1458 self.noise1 = "plats/medplat2.wav";
1468 self.pos1 = self.origin;
1469 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1471 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1472 // but spawn in the open position
1473 if (self.spawnflags & DOOR_START_OPEN)
1474 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1476 self.state = STATE_BOTTOM;
1480 self.takedamage = DAMAGE_YES;
1481 self.event_damage = door_damage;
1487 self.touch = door_touch;
1489 // LinkDoors can't be done until all of the doors have been spawned, so
1490 // the sizes can be detected properly.
1491 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1493 self.reset = door_reset;
1496 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1497 if two doors touch, they are assumed to be connected and operate as a unit.
1499 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1501 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1502 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1503 must have set trigger_reverse to 1.
1504 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1506 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).
1508 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1509 "angle" determines the destination angle for opening. negative values reverse the direction.
1510 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1511 "health" if set, door must be shot open
1512 "speed" movement speed (100 default)
1513 "wait" wait before returning (3 default, -1 = never return)
1514 "dmg" damage to inflict when blocked (2 default)
1521 FIXME: only one sound set available at the time being
1524 void door_rotating_reset()
1526 self.angles = self.pos1;
1527 self.avelocity = '0 0 0';
1528 self.state = STATE_BOTTOM;
1529 self.think = SUB_Null;
1532 void door_rotating_init_startopen()
1534 self.angles = self.movedir;
1535 self.pos2 = '0 0 0';
1536 self.pos1 = self.movedir;
1540 void spawnfunc_func_door_rotating()
1543 //if (!self.deathtype) // map makers can override this
1544 // self.deathtype = " got in the way";
1546 // I abuse "movedir" for denoting the axis for now
1547 if (self.spawnflags & 64) // X (untested)
1548 self.movedir = '0 0 1';
1549 else if (self.spawnflags & 128) // Y (untested)
1550 self.movedir = '1 0 0';
1552 self.movedir = '0 1 0';
1554 if (self.angles_y==0) self.angles_y = 90;
1556 self.movedir = self.movedir * self.angles_y;
1557 self.angles = '0 0 0';
1559 self.max_health = self.health;
1560 self.avelocity = self.movedir;
1561 if not(InitMovingBrushTrigger())
1563 self.velocity = '0 0 0';
1564 //self.effects |= EF_LOWPRECISION;
1565 self.classname = "door_rotating";
1567 self.blocked = door_blocked;
1568 self.use = door_use;
1570 if(self.spawnflags & 8)
1573 if(self.dmg && (!self.message))
1574 self.message = "was squished";
1575 if(self.dmg && (!self.message2))
1576 self.message2 = "was squished by";
1578 if (self.sounds > 0)
1580 precache_sound ("plats/medplat1.wav");
1581 precache_sound ("plats/medplat2.wav");
1582 self.noise2 = "plats/medplat1.wav";
1583 self.noise1 = "plats/medplat2.wav";
1590 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1592 self.pos1 = '0 0 0';
1593 self.pos2 = self.movedir;
1595 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1596 // but spawn in the open position
1597 if (self.spawnflags & DOOR_START_OPEN)
1598 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1600 self.state = STATE_BOTTOM;
1604 self.takedamage = DAMAGE_YES;
1605 self.event_damage = door_damage;
1611 self.touch = door_touch;
1613 // LinkDoors can't be done until all of the doors have been spawned, so
1614 // the sizes can be detected properly.
1615 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1617 self.reset = door_rotating_reset;
1621 =============================================================================
1625 =============================================================================
1628 void() fd_secret_move1;
1629 void() fd_secret_move2;
1630 void() fd_secret_move3;
1631 void() fd_secret_move4;
1632 void() fd_secret_move5;
1633 void() fd_secret_move6;
1634 void() fd_secret_done;
1636 float SECRET_OPEN_ONCE = 1; // stays open
1637 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1638 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1639 float SECRET_NO_SHOOT = 8; // only opened by trigger
1640 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1643 void fd_secret_use()
1646 string message_save;
1648 self.health = 10000;
1649 self.bot_attack = TRUE;
1651 // exit if still moving around...
1652 if (self.origin != self.oldorigin)
1655 message_save = self.message;
1656 self.message = ""; // no more message
1657 SUB_UseTargets(); // fire all targets / killtargets
1658 self.message = message_save;
1660 self.velocity = '0 0 0';
1662 // Make a sound, wait a little...
1664 if (self.noise1 != "")
1665 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1666 self.nextthink = self.ltime + 0.1;
1668 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1669 makevectors(self.mangle);
1673 if (self.spawnflags & SECRET_1ST_DOWN)
1674 self.t_width = fabs(v_up * self.size);
1676 self.t_width = fabs(v_right * self.size);
1680 self.t_length = fabs(v_forward * self.size);
1682 if (self.spawnflags & SECRET_1ST_DOWN)
1683 self.dest1 = self.origin - v_up * self.t_width;
1685 self.dest1 = self.origin + v_right * (self.t_width * temp);
1687 self.dest2 = self.dest1 + v_forward * self.t_length;
1688 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1689 if (self.noise2 != "")
1690 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1693 // Wait after first movement...
1694 void fd_secret_move1()
1696 self.nextthink = self.ltime + 1.0;
1697 self.think = fd_secret_move2;
1698 if (self.noise3 != "")
1699 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1702 // Start moving sideways w/sound...
1703 void fd_secret_move2()
1705 if (self.noise2 != "")
1706 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1707 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1710 // Wait here until time to go back...
1711 void fd_secret_move3()
1713 if (self.noise3 != "")
1714 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1715 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1717 self.nextthink = self.ltime + self.wait;
1718 self.think = fd_secret_move4;
1723 void fd_secret_move4()
1725 if (self.noise2 != "")
1726 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1727 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1731 void fd_secret_move5()
1733 self.nextthink = self.ltime + 1.0;
1734 self.think = fd_secret_move6;
1735 if (self.noise3 != "")
1736 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1739 void fd_secret_move6()
1741 if (self.noise2 != "")
1742 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1743 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1746 void fd_secret_done()
1748 if (self.spawnflags&SECRET_YES_SHOOT)
1750 self.health = 10000;
1751 self.takedamage = DAMAGE_YES;
1752 //self.th_pain = fd_secret_use;
1754 if (self.noise3 != "")
1755 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1758 void secret_blocked()
1760 if (time < self.attack_finished_single)
1762 self.attack_finished_single = time + 0.5;
1763 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1775 if not(other.iscreature)
1777 if (self.attack_finished_single > time)
1780 self.attack_finished_single = time + 2;
1784 if (other.flags & FL_CLIENT)
1785 centerprint (other, self.message);
1786 play2(other, "misc/talk.wav");
1792 if (self.spawnflags&SECRET_YES_SHOOT)
1794 self.health = 10000;
1795 self.takedamage = DAMAGE_YES;
1797 setorigin(self, self.oldorigin);
1798 self.think = SUB_Null;
1801 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1802 Basic secret door. Slides back, then to the side. Angle determines direction.
1803 wait = # of seconds before coming back
1804 1st_left = 1st move is left of arrow
1805 1st_down = 1st move is down from arrow
1806 always_shoot = even if targeted, keep shootable
1807 t_width = override WIDTH to move back (or height if going down)
1808 t_length = override LENGTH to move sideways
1809 "dmg" damage to inflict when blocked (2 default)
1811 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1818 void spawnfunc_func_door_secret()
1820 /*if (!self.deathtype) // map makers can override this
1821 self.deathtype = " got in the way";*/
1827 self.mangle = self.angles;
1828 self.angles = '0 0 0';
1829 self.classname = "door";
1830 if not(InitMovingBrushTrigger())
1832 self.effects |= EF_LOWPRECISION;
1834 self.touch = secret_touch;
1835 self.blocked = secret_blocked;
1837 self.use = fd_secret_use;
1842 self.spawnflags |= SECRET_YES_SHOOT;
1844 if(self.spawnflags&SECRET_YES_SHOOT)
1846 self.health = 10000;
1847 self.takedamage = DAMAGE_YES;
1848 self.event_damage = fd_secret_use;
1850 self.oldorigin = self.origin;
1852 self.wait = 5; // 5 seconds before closing
1854 self.reset = secret_reset;
1858 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1859 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1860 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
1861 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1862 height: amplitude modifier (default 32)
1863 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1864 noise: path/name of looping .wav file to play.
1865 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1869 void func_fourier_controller_think()
1874 self.nextthink = time + 0.1;
1875 if not (self.owner.active == ACTIVE_ACTIVE)
1877 self.owner.velocity = '0 0 0';
1882 n = floor((tokenize_console(self.owner.netname)) / 5);
1883 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1885 v = self.owner.destvec;
1887 for(i = 0; i < n; ++i)
1889 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1890 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;
1893 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1894 // * 10 so it will arrive in 0.1 sec
1895 self.owner.velocity = (v - self.owner.origin) * 10;
1898 void spawnfunc_func_fourier()
1901 if (self.noise != "")
1903 precache_sound(self.noise);
1904 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1911 self.destvec = self.origin;
1912 self.cnt = 360 / self.speed;
1914 self.blocked = generic_plat_blocked;
1915 if(self.dmg & (!self.message))
1916 self.message = " was squished";
1917 if(self.dmg && (!self.message2))
1918 self.message2 = "was squished by";
1919 if(self.dmg && (!self.dmgtime))
1920 self.dmgtime = 0.25;
1921 self.dmgtime2 = time;
1923 if(self.netname == "")
1924 self.netname = "1 0 0 0 1";
1926 if not(InitMovingBrushTrigger())
1929 self.active = ACTIVE_ACTIVE;
1931 // wait for targets to spawn
1932 controller = spawn();
1933 controller.classname = "func_fourier_controller";
1934 controller.owner = self;
1935 controller.nextthink = time + 1;
1936 controller.think = func_fourier_controller_think;
1937 self.nextthink = self.ltime + 999999999;
1938 self.think = SUB_Null;
1940 // Savage: Reduce bandwith, critical on e.g. nexdm02
1941 self.effects |= EF_LOWPRECISION;
1943 // TODO make a reset function for this one
1946 // reusing some fields havocbots declared
1947 .entity wp00, wp01, wp02, wp03;
1949 .float targetfactor, target2factor, target3factor, target4factor;
1950 .vector targetnormal, target2normal, target3normal, target4normal;
1952 vector func_vectormamamam_origin(entity o, float t)
1964 p = e.origin + t * e.velocity;
1966 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1968 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1974 p = e.origin + t * e.velocity;
1976 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1978 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1984 p = e.origin + t * e.velocity;
1986 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1988 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1994 p = e.origin + t * e.velocity;
1996 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1998 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2004 void func_vectormamamam_controller_think()
2006 self.nextthink = time + 0.1;
2008 if not (self.owner.active == ACTIVE_ACTIVE)
2010 self.owner.velocity = '0 0 0';
2014 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2015 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2018 void func_vectormamamam_findtarget()
2020 if(self.target != "")
2021 self.wp00 = find(world, targetname, self.target);
2023 if(self.target2 != "")
2024 self.wp01 = find(world, targetname, self.target2);
2026 if(self.target3 != "")
2027 self.wp02 = find(world, targetname, self.target3);
2029 if(self.target4 != "")
2030 self.wp03 = find(world, targetname, self.target4);
2032 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2033 objerror("No reference entity found, so there is nothing to move. Aborting.");
2035 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2038 controller = spawn();
2039 controller.classname = "func_vectormamamam_controller";
2040 controller.owner = self;
2041 controller.nextthink = time + 1;
2042 controller.think = func_vectormamamam_controller_think;
2045 void spawnfunc_func_vectormamamam()
2047 if (self.noise != "")
2049 precache_sound(self.noise);
2050 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2053 if(!self.targetfactor)
2054 self.targetfactor = 1;
2056 if(!self.target2factor)
2057 self.target2factor = 1;
2059 if(!self.target3factor)
2060 self.target3factor = 1;
2062 if(!self.target4factor)
2063 self.target4factor = 1;
2065 if(vlen(self.targetnormal))
2066 self.targetnormal = normalize(self.targetnormal);
2068 if(vlen(self.target2normal))
2069 self.target2normal = normalize(self.target2normal);
2071 if(vlen(self.target3normal))
2072 self.target3normal = normalize(self.target3normal);
2074 if(vlen(self.target4normal))
2075 self.target4normal = normalize(self.target4normal);
2077 self.blocked = generic_plat_blocked;
2078 if(self.dmg & (!self.message))
2079 self.message = " was squished";
2080 if(self.dmg && (!self.message2))
2081 self.message2 = "was squished by";
2082 if(self.dmg && (!self.dmgtime))
2083 self.dmgtime = 0.25;
2084 self.dmgtime2 = time;
2086 if(self.netname == "")
2087 self.netname = "1 0 0 0 1";
2089 if not(InitMovingBrushTrigger())
2092 // wait for targets to spawn
2093 self.nextthink = self.ltime + 999999999;
2094 self.think = SUB_Null;
2096 // Savage: Reduce bandwith, critical on e.g. nexdm02
2097 self.effects |= EF_LOWPRECISION;
2099 self.active = ACTIVE_ACTIVE;
2101 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2104 void conveyor_think()
2108 // set myself as current conveyor where possible
2109 for(e = world; (e = findentity(e, conveyor, self)); )
2114 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2115 if(!e.conveyor.state)
2118 vector emin = e.absmin;
2119 vector emax = e.absmax;
2120 if(self.solid == SOLID_BSP)
2125 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2126 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2130 for(e = world; (e = findentity(e, conveyor, self)); )
2132 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2133 continue; // done in SV_PlayerPhysics
2135 setorigin(e, e.origin + self.movedir * sys_frametime);
2136 move_out_of_solid(e);
2137 UpdateCSQCProjectile(e);
2139 // stupid conveyor code
2140 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2141 if(trace_fraction > 0)
2142 setorigin(e, trace_endpos);
2147 self.nextthink = time;
2152 self.state = !self.state;
2155 void conveyor_reset()
2157 self.state = (self.spawnflags & 1);
2160 void conveyor_init()
2164 self.movedir = self.movedir * self.speed;
2165 self.think = conveyor_think;
2166 self.nextthink = time;
2169 self.use = conveyor_use;
2170 self.reset = conveyor_reset;
2177 void spawnfunc_trigger_conveyor()
2184 void spawnfunc_func_conveyor()
2187 InitMovingBrushTrigger();
2188 self.movetype = MOVETYPE_NONE;