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 .float platmovetype_start_default, platmovetype_end_default;
186 float set_platmovetype(entity e, string s)
188 // sets platmovetype_start and platmovetype_end based on a string consisting of two values
191 n = tokenize_console(s);
193 e.platmovetype_start = stof(argv(0));
195 e.platmovetype_start = 0;
198 e.platmovetype_end = stof(argv(1));
200 e.platmovetype_end = e.platmovetype_start;
203 if(argv(2) == "force")
204 return TRUE; // no checking, return immediately
206 if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
208 objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
215 void spawnfunc_path_corner()
217 // setup values for overriding train movement
218 // if a second value does not exist, both start and end speeds are the single value specified
219 if(!set_platmovetype(self, self.platmovetype))
222 void spawnfunc_func_plat()
224 if (self.sounds == 0)
227 if(self.spawnflags & 4)
230 if(self.dmg && (!self.message))
231 self.message = "was squished";
232 if(self.dmg && (!self.message2))
233 self.message2 = "was squished by";
235 if (self.sounds == 1)
237 precache_sound ("plats/plat1.wav");
238 precache_sound ("plats/plat2.wav");
239 self.noise = "plats/plat1.wav";
240 self.noise1 = "plats/plat2.wav";
243 if (self.sounds == 2)
245 precache_sound ("plats/medplat1.wav");
246 precache_sound ("plats/medplat2.wav");
247 self.noise = "plats/medplat1.wav";
248 self.noise1 = "plats/medplat2.wav";
253 precache_sound (self.sound1);
254 self.noise = self.sound1;
258 precache_sound (self.sound2);
259 self.noise1 = self.sound2;
262 self.mangle = self.angles;
263 self.angles = '0 0 0';
265 self.classname = "plat";
266 if not(InitMovingBrushTrigger())
268 self.effects |= EF_LOWPRECISION;
269 setsize (self, self.mins , self.maxs);
271 self.blocked = plat_crush;
278 self.height = self.size_z - self.lip;
280 self.pos1 = self.origin;
281 self.pos2 = self.origin;
282 self.pos2_z = self.origin_z - self.height;
284 self.reset = plat_reset;
287 plat_spawn_inside_trigger (); // the "start moving" trigger
290 .float train_wait_turning;
301 // if turning is enabled, the train will turn toward the next point while waiting
302 if(self.platmovetype_turn && !self.train_wait_turning)
306 targ = find(world, targetname, self.target);
307 if((self.spawnflags & 1) && targ.curvetarget)
308 cp = find(world, targetname, targ.curvetarget);
312 if(cp) // bezier curves movement
313 ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
314 else // linear movement
315 ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
316 ang = vectoangles(ang);
317 ang_x = -ang_x; // flip up / down orientation
319 if(self.wait >= 0) // slow turning
321 SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
322 self.train_wait_turning = TRUE;
325 else // instant turning
330 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
332 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
334 self.train_wait_turning = FALSE;
339 self.think = train_next;
340 self.nextthink = self.ltime + self.wait;
349 targ = find(world, targetname, self.target);
350 self.target = targ.target;
351 if (self.spawnflags & 1)
355 cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
356 cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
362 objerror("train_next: no next target");
363 self.wait = targ.wait;
367 if(targ.platmovetype)
369 // this path_corner contains a movetype overrider, apply it
370 self.platmovetype_start = targ.platmovetype_start;
371 self.platmovetype_end = targ.platmovetype_end;
375 // this path_corner doesn't contain a movetype overrider, use the train's defaults
376 self.platmovetype_start = self.platmovetype_start_default;
377 self.platmovetype_end = self.platmovetype_end_default;
383 SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
385 SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
390 SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
392 SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
396 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
399 void func_train_find()
402 targ = find(world, targetname, self.target);
403 self.target = targ.target;
405 objerror("func_train_find: no next target");
406 setorigin(self, targ.origin - self.view_ofs);
407 self.nextthink = self.ltime + 1;
408 self.think = train_next;
411 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
412 Ridable platform, targets spawnfunc_path_corner path to follow.
413 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
414 target : targetname of first spawnfunc_path_corner (starts here)
416 void spawnfunc_func_train()
418 if (self.noise != "")
419 precache_sound(self.noise);
422 objerror("func_train without a target");
426 if (self.spawnflags & 2)
428 self.platmovetype_turn = TRUE;
429 self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
432 self.view_ofs = self.mins;
434 if not(InitMovingBrushTrigger())
436 self.effects |= EF_LOWPRECISION;
438 // wait for targets to spawn
439 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
441 self.blocked = generic_plat_blocked;
442 if(self.dmg & (!self.message))
443 self.message = " was squished";
444 if(self.dmg && (!self.message2))
445 self.message2 = "was squished by";
446 if(self.dmg && (!self.dmgtime))
448 self.dmgtime2 = time;
450 if(!set_platmovetype(self, self.platmovetype))
452 self.platmovetype_start_default = self.platmovetype_start;
453 self.platmovetype_end_default = self.platmovetype_end;
455 // TODO make a reset function for this one
458 void func_rotating_setactive(float astate)
461 if (astate == ACTIVE_TOGGLE)
463 if(self.active == ACTIVE_ACTIVE)
464 self.active = ACTIVE_NOT;
466 self.active = ACTIVE_ACTIVE;
469 self.active = astate;
471 if(self.active == ACTIVE_NOT)
472 self.avelocity = '0 0 0';
474 self.avelocity = self.pos1;
477 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
478 Brush model that spins in place on one axis (default Z).
479 speed : speed to rotate (in degrees per second)
480 noise : path/name of looping .wav file to play.
481 dmg : Do this mutch dmg every .dmgtime intervall when blocked
485 void spawnfunc_func_rotating()
487 if (self.noise != "")
489 precache_sound(self.noise);
490 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
493 self.active = ACTIVE_ACTIVE;
494 self.setactive = func_rotating_setactive;
498 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
499 if (self.spawnflags & 4) // X (untested)
500 self.avelocity = '0 0 1' * self.speed;
501 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
502 else if (self.spawnflags & 8) // Y (untested)
503 self.avelocity = '1 0 0' * self.speed;
504 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
506 self.avelocity = '0 1 0' * self.speed;
508 self.pos1 = self.avelocity;
510 if(self.dmg & (!self.message))
511 self.message = " was squished";
512 if(self.dmg && (!self.message2))
513 self.message2 = "was squished by";
516 if(self.dmg && (!self.dmgtime))
519 self.dmgtime2 = time;
521 if not(InitMovingBrushTrigger())
523 // no EF_LOWPRECISION here, as rounding angles is bad
525 self.blocked = generic_plat_blocked;
527 // wait for targets to spawn
528 self.nextthink = self.ltime + 999999999;
529 self.think = SUB_Null;
531 // TODO make a reset function for this one
535 void func_bobbing_controller_think()
538 self.nextthink = time + 0.1;
540 if not (self.owner.active == ACTIVE_ACTIVE)
542 self.owner.velocity = '0 0 0';
546 // calculate sinewave using makevectors
547 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
548 v = self.owner.destvec + self.owner.movedir * v_forward_y;
549 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
550 // * 10 so it will arrive in 0.1 sec
551 self.owner.velocity = (v - self.owner.origin) * 10;
554 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
555 Brush model that moves back and forth on one axis (default Z).
556 speed : how long one cycle takes in seconds (default 4)
557 height : how far the cycle moves (default 32)
558 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
559 noise : path/name of looping .wav file to play.
560 dmg : Do this mutch dmg every .dmgtime intervall when blocked
563 void spawnfunc_func_bobbing()
566 if (self.noise != "")
568 precache_sound(self.noise);
569 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
575 // center of bobbing motion
576 self.destvec = self.origin;
577 // time scale to get degrees
578 self.cnt = 360 / self.speed;
580 self.active = ACTIVE_ACTIVE;
582 // damage when blocked
583 self.blocked = generic_plat_blocked;
584 if(self.dmg & (!self.message))
585 self.message = " was squished";
586 if(self.dmg && (!self.message2))
587 self.message2 = "was squished by";
588 if(self.dmg && (!self.dmgtime))
590 self.dmgtime2 = time;
593 if (self.spawnflags & 1) // X
594 self.movedir = '1 0 0' * self.height;
595 else if (self.spawnflags & 2) // Y
596 self.movedir = '0 1 0' * self.height;
598 self.movedir = '0 0 1' * self.height;
600 if not(InitMovingBrushTrigger())
603 // wait for targets to spawn
604 controller = spawn();
605 controller.classname = "func_bobbing_controller";
606 controller.owner = self;
607 controller.nextthink = time + 1;
608 controller.think = func_bobbing_controller_think;
609 self.nextthink = self.ltime + 999999999;
610 self.think = SUB_Null;
612 // Savage: Reduce bandwith, critical on e.g. nexdm02
613 self.effects |= EF_LOWPRECISION;
615 // TODO make a reset function for this one
619 void func_pendulum_controller_think()
622 self.nextthink = time + 0.1;
624 if not (self.owner.active == ACTIVE_ACTIVE)
626 self.owner.avelocity_x = 0;
630 // calculate sinewave using makevectors
631 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
632 v = self.owner.speed * v_forward_y + self.cnt;
633 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
635 // * 10 so it will arrive in 0.1 sec
636 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
640 void spawnfunc_func_pendulum()
643 if (self.noise != "")
645 precache_sound(self.noise);
646 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
649 self.active = ACTIVE_ACTIVE;
651 // keys: angle, speed, phase, noise, freq
655 // not initializing self.dmg to 2, to allow damageless pendulum
657 if(self.dmg & (!self.message))
658 self.message = " was squished";
659 if(self.dmg && (!self.message2))
660 self.message2 = "was squished by";
661 if(self.dmg && (!self.dmgtime))
663 self.dmgtime2 = time;
665 self.blocked = generic_plat_blocked;
667 self.avelocity_z = 0.0000001;
668 if not(InitMovingBrushTrigger())
673 // find pendulum length (same formula as Q3A)
674 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
677 // copy initial angle
678 self.cnt = self.angles_z;
680 // wait for targets to spawn
681 controller = spawn();
682 controller.classname = "func_pendulum_controller";
683 controller.owner = self;
684 controller.nextthink = time + 1;
685 controller.think = func_pendulum_controller_think;
686 self.nextthink = self.ltime + 999999999;
687 self.think = SUB_Null;
689 //self.effects |= EF_LOWPRECISION;
691 // TODO make a reset function for this one
694 // button and multiple button
697 void() button_return;
701 self.state = STATE_TOP;
702 self.nextthink = self.ltime + self.wait;
703 self.think = button_return;
704 activator = self.enemy;
706 self.frame = 1; // use alternate textures
711 self.state = STATE_BOTTOM;
716 self.state = STATE_DOWN;
717 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
718 self.frame = 0; // use normal textures
720 self.takedamage = DAMAGE_YES; // can be shot again
724 void button_blocked()
726 // do nothing, just don't come all the way back out
732 self.health = self.max_health;
733 self.takedamage = DAMAGE_NO; // will be reset upon return
735 if (self.state == STATE_UP || self.state == STATE_TOP)
738 if (self.noise != "")
739 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
741 self.state = STATE_UP;
742 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
747 self.health = self.max_health;
748 setorigin(self, self.pos1);
749 self.frame = 0; // use normal textures
750 self.state = STATE_BOTTOM;
752 self.takedamage = DAMAGE_YES; // can be shot again
757 // if (activator.classname != "player")
759 // dprint(activator.classname);
760 // dprint(" triggered a button\n");
763 if not (self.active == ACTIVE_ACTIVE)
766 self.enemy = activator;
772 // if (activator.classname != "player")
774 // dprint(activator.classname);
775 // dprint(" touched a button\n");
779 if not(other.iscreature)
781 if(other.velocity * self.movedir < 0)
785 self.enemy = other.owner;
789 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
791 if(self.spawnflags & DOOR_NOSPLASH)
792 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
794 self.health = self.health - damage;
795 if (self.health <= 0)
797 // if (activator.classname != "player")
799 // dprint(activator.classname);
800 // dprint(" killed a button\n");
802 self.enemy = damage_attacker;
808 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
809 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.
811 "angle" determines the opening direction
812 "target" all entities with a matching targetname will be used
813 "speed" override the default 40 speed
814 "wait" override the default 1 second wait (-1 = never return)
815 "lip" override the default 4 pixel lip remaining at end of move
816 "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
823 void spawnfunc_func_button()
827 if not(InitMovingBrushTrigger())
829 self.effects |= EF_LOWPRECISION;
831 self.blocked = button_blocked;
832 self.use = button_use;
834 // if (self.health == 0) // all buttons are now shootable
838 self.max_health = self.health;
839 self.event_damage = button_damage;
840 self.takedamage = DAMAGE_YES;
843 self.touch = button_touch;
853 precache_sound(self.noise);
855 self.active = ACTIVE_ACTIVE;
857 self.pos1 = self.origin;
858 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
859 self.flags |= FL_NOTARGET;
865 float DOOR_START_OPEN = 1;
866 float DOOR_DONT_LINK = 4;
867 float DOOR_TOGGLE = 32;
871 Doors are similar to buttons, but can spawn a fat trigger field around them
872 to open without a touch, and they link together to form simultanious
875 Door.owner is the master door. If there is only one door, it points to itself.
876 If multiple doors, all will point to a single one.
878 Door.enemy chains from the master door through all doors linked in the chain.
883 =============================================================================
887 =============================================================================
892 void() door_rotating_go_down;
893 void() door_rotating_go_up;
898 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
899 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
902 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
903 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
905 //Dont chamge direction for dead or dying stuff
906 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
909 if (self.state == STATE_DOWN)
910 if (self.classname == "door")
915 door_rotating_go_up ();
918 if (self.classname == "door")
923 door_rotating_go_down ();
927 //gib dying stuff just to make sure
928 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
929 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
933 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
934 // if a door has a negative wait, it would never come back if blocked,
935 // so let it just squash the object to death real fast
936 /* if (self.wait >= 0)
938 if (self.state == STATE_DOWN)
949 if (self.noise1 != "")
950 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
951 self.state = STATE_TOP;
952 if (self.spawnflags & DOOR_TOGGLE)
953 return; // don't come down automatically
954 if (self.classname == "door")
956 self.think = door_go_down;
959 self.think = door_rotating_go_down;
961 self.nextthink = self.ltime + self.wait;
964 void door_hit_bottom()
966 if (self.noise1 != "")
967 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
968 self.state = STATE_BOTTOM;
973 if (self.noise2 != "")
974 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
977 self.takedamage = DAMAGE_YES;
978 self.health = self.max_health;
981 self.state = STATE_DOWN;
982 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
987 if (self.state == STATE_UP)
988 return; // already going up
990 if (self.state == STATE_TOP)
991 { // reset top wait time
992 self.nextthink = self.ltime + self.wait;
996 if (self.noise2 != "")
997 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
998 self.state = STATE_UP;
999 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
1002 oldmessage = self.message;
1005 self.message = oldmessage;
1011 =============================================================================
1013 ACTIVATION FUNCTIONS
1015 =============================================================================
1018 float door_check_keys(void) {
1028 if not(door.itemkeys)
1031 // this door require a key
1032 // only a player can have a key
1033 if (other.classname != "player")
1036 if (item_keys_usekey(door, other)) {
1037 // some keys were used
1038 if (other.key_door_messagetime <= time) {
1039 play2(other, "misc/talk.wav");
1040 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
1041 other.key_door_messagetime = time + 2;
1044 // no keys were used
1045 if (other.key_door_messagetime <= time) {
1046 play2(other, "misc/talk.wav");
1047 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
1048 other.key_door_messagetime = time + 2;
1052 if (door.itemkeys) {
1053 // door is now unlocked
1054 play2(other, "misc/talk.wav");
1055 centerprint(other, "Door unlocked!");
1067 if (self.owner != self)
1068 objerror ("door_fire: self.owner != self");
1072 if (self.spawnflags & DOOR_TOGGLE)
1074 if (self.state == STATE_UP || self.state == STATE_TOP)
1079 if (self.classname == "door")
1085 door_rotating_go_down ();
1088 } while ( (self != starte) && (self != world) );
1094 // trigger all paired doors
1098 if (self.classname == "door")
1103 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1104 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1106 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1107 self.pos2 = '0 0 0' - self.pos2;
1109 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1110 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1111 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1113 door_rotating_go_up ();
1117 } while ( (self != starte) && (self != world) );
1126 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1138 void door_trigger_touch()
1140 if (other.health < 1)
1141 if not(other.iscreature && other.deadflag == DEAD_NO)
1144 if (time < self.attack_finished_single)
1147 // check if door is locked
1148 if (!door_check_keys())
1151 self.attack_finished_single = time + 1;
1160 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1163 if(self.spawnflags & DOOR_NOSPLASH)
1164 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1166 self.health = self.health - damage;
1168 if (self.itemkeys) {
1169 // don't allow opening doors through damage if keys are required
1173 if (self.health <= 0)
1177 self.health = self.max_health;
1178 self.takedamage = DAMAGE_NO; // wil be reset upon return
1194 if(other.classname != "player")
1196 if (self.owner.attack_finished_single > time)
1199 self.owner.attack_finished_single = time + 2;
1201 if (!(self.owner.dmg) && (self.owner.message != ""))
1203 if (other.flags & FL_CLIENT)
1204 centerprint (other, self.owner.message);
1205 play2(other, "misc/talk.wav");
1210 void door_generic_plat_blocked()
1213 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1214 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1217 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1218 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1220 //Dont chamge direction for dead or dying stuff
1221 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1224 if (self.state == STATE_DOWN)
1225 door_rotating_go_up ();
1227 door_rotating_go_down ();
1230 //gib dying stuff just to make sure
1231 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1232 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1236 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1237 // if a door has a negative wait, it would never come back if blocked,
1238 // so let it just squash the object to death real fast
1239 /* if (self.wait >= 0)
1241 if (self.state == STATE_DOWN)
1242 door_rotating_go_up ();
1244 door_rotating_go_down ();
1250 void door_rotating_hit_top()
1252 if (self.noise1 != "")
1253 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1254 self.state = STATE_TOP;
1255 if (self.spawnflags & DOOR_TOGGLE)
1256 return; // don't come down automatically
1257 self.think = door_rotating_go_down;
1258 self.nextthink = self.ltime + self.wait;
1261 void door_rotating_hit_bottom()
1263 if (self.noise1 != "")
1264 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1265 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1267 self.pos2 = '0 0 0' - self.pos2;
1270 self.state = STATE_BOTTOM;
1273 void door_rotating_go_down()
1275 if (self.noise2 != "")
1276 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1277 if (self.max_health)
1279 self.takedamage = DAMAGE_YES;
1280 self.health = self.max_health;
1283 self.state = STATE_DOWN;
1284 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1287 void door_rotating_go_up()
1289 if (self.state == STATE_UP)
1290 return; // already going up
1292 if (self.state == STATE_TOP)
1293 { // reset top wait time
1294 self.nextthink = self.ltime + self.wait;
1297 if (self.noise2 != "")
1298 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1299 self.state = STATE_UP;
1300 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1303 oldmessage = self.message;
1306 self.message = oldmessage;
1313 =============================================================================
1317 =============================================================================
1321 entity spawn_field(vector fmins, vector fmaxs)
1327 trigger.classname = "doortriggerfield";
1328 trigger.movetype = MOVETYPE_NONE;
1329 trigger.solid = SOLID_TRIGGER;
1330 trigger.owner = self;
1331 trigger.touch = door_trigger_touch;
1335 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1340 float EntitiesTouching(entity e1, entity e2)
1342 if (e1.absmin_x > e2.absmax_x)
1344 if (e1.absmin_y > e2.absmax_y)
1346 if (e1.absmin_z > e2.absmax_z)
1348 if (e1.absmax_x < e2.absmin_x)
1350 if (e1.absmax_y < e2.absmin_y)
1352 if (e1.absmax_z < e2.absmin_z)
1368 vector cmins, cmaxs;
1371 return; // already linked by another door
1372 if (self.spawnflags & 4)
1374 self.owner = self.enemy = self;
1382 self.trigger_field = spawn_field(self.absmin, self.absmax);
1384 return; // don't want to link this door
1387 cmins = self.absmin;
1388 cmaxs = self.absmax;
1395 self.owner = starte; // master door
1398 starte.health = self.health;
1400 starte.targetname = self.targetname;
1401 if (self.message != "")
1402 starte.message = self.message;
1404 t = find(t, classname, self.classname);
1407 self.enemy = starte; // make the chain a loop
1409 // shootable, or triggered doors just needed the owner/enemy links,
1410 // they don't spawn a field
1421 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1426 if (EntitiesTouching(self,t))
1429 objerror ("cross connected doors");
1434 if (t.absmin_x < cmins_x)
1435 cmins_x = t.absmin_x;
1436 if (t.absmin_y < cmins_y)
1437 cmins_y = t.absmin_y;
1438 if (t.absmin_z < cmins_z)
1439 cmins_z = t.absmin_z;
1440 if (t.absmax_x > cmaxs_x)
1441 cmaxs_x = t.absmax_x;
1442 if (t.absmax_y > cmaxs_y)
1443 cmaxs_y = t.absmax_y;
1444 if (t.absmax_z > cmaxs_z)
1445 cmaxs_z = t.absmax_z;
1452 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1453 if two doors touch, they are assumed to be connected and operate as a unit.
1455 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1457 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).
1459 GOLD_KEY causes the door to open only if the activator holds a gold key.
1461 SILVER_KEY causes the door to open only if the activator holds a silver key.
1463 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1464 "angle" determines the opening direction
1465 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1466 "health" if set, door must be shot open
1467 "speed" movement speed (100 default)
1468 "wait" wait before returning (3 default, -1 = never return)
1469 "lip" lip remaining at end of move (8 default)
1470 "dmg" damage to inflict when blocked (2 default)
1477 FIXME: only one sound set available at the time being
1481 void door_init_startopen()
1483 setorigin (self, self.pos2);
1484 self.pos2 = self.pos1;
1485 self.pos1 = self.origin;
1490 setorigin(self, self.pos1);
1491 self.velocity = '0 0 0';
1492 self.state = STATE_BOTTOM;
1493 self.think = SUB_Null;
1496 // spawnflags require key (for now only func_door)
1497 #define SPAWNFLAGS_GOLD_KEY 8
1498 #define SPAWNFLAGS_SILVER_KEY 16
1499 void spawnfunc_func_door()
1501 // Quake 1 keys compatibility
1502 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1503 self.itemkeys |= ITEM_KEY_BIT(0);
1504 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1505 self.itemkeys |= ITEM_KEY_BIT(1);
1507 //if (!self.deathtype) // map makers can override this
1508 // self.deathtype = " got in the way";
1511 self.max_health = self.health;
1512 if not(InitMovingBrushTrigger())
1514 self.effects |= EF_LOWPRECISION;
1515 self.classname = "door";
1517 self.blocked = door_blocked;
1518 self.use = door_use;
1520 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1521 // if(self.spawnflags & 8)
1522 // self.dmg = 10000;
1524 if(self.dmg && (!self.message))
1525 self.message = "was squished";
1526 if(self.dmg && (!self.message2))
1527 self.message2 = "was squished by";
1529 if (self.sounds > 0)
1531 precache_sound ("plats/medplat1.wav");
1532 precache_sound ("plats/medplat2.wav");
1533 self.noise2 = "plats/medplat1.wav";
1534 self.noise1 = "plats/medplat2.wav";
1544 self.pos1 = self.origin;
1545 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1547 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1548 // but spawn in the open position
1549 if (self.spawnflags & DOOR_START_OPEN)
1550 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1552 self.state = STATE_BOTTOM;
1556 self.takedamage = DAMAGE_YES;
1557 self.event_damage = door_damage;
1563 self.touch = door_touch;
1565 // LinkDoors can't be done until all of the doors have been spawned, so
1566 // the sizes can be detected properly.
1567 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1569 self.reset = door_reset;
1572 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1573 if two doors touch, they are assumed to be connected and operate as a unit.
1575 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1577 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1578 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1579 must have set trigger_reverse to 1.
1580 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1582 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).
1584 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1585 "angle" determines the destination angle for opening. negative values reverse the direction.
1586 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1587 "health" if set, door must be shot open
1588 "speed" movement speed (100 default)
1589 "wait" wait before returning (3 default, -1 = never return)
1590 "dmg" damage to inflict when blocked (2 default)
1597 FIXME: only one sound set available at the time being
1600 void door_rotating_reset()
1602 self.angles = self.pos1;
1603 self.avelocity = '0 0 0';
1604 self.state = STATE_BOTTOM;
1605 self.think = SUB_Null;
1608 void door_rotating_init_startopen()
1610 self.angles = self.movedir;
1611 self.pos2 = '0 0 0';
1612 self.pos1 = self.movedir;
1616 void spawnfunc_func_door_rotating()
1619 //if (!self.deathtype) // map makers can override this
1620 // self.deathtype = " got in the way";
1622 // I abuse "movedir" for denoting the axis for now
1623 if (self.spawnflags & 64) // X (untested)
1624 self.movedir = '0 0 1';
1625 else if (self.spawnflags & 128) // Y (untested)
1626 self.movedir = '1 0 0';
1628 self.movedir = '0 1 0';
1630 if (self.angles_y==0) self.angles_y = 90;
1632 self.movedir = self.movedir * self.angles_y;
1633 self.angles = '0 0 0';
1635 self.max_health = self.health;
1636 self.avelocity = self.movedir;
1637 if not(InitMovingBrushTrigger())
1639 self.velocity = '0 0 0';
1640 //self.effects |= EF_LOWPRECISION;
1641 self.classname = "door_rotating";
1643 self.blocked = door_blocked;
1644 self.use = door_use;
1646 if(self.spawnflags & 8)
1649 if(self.dmg && (!self.message))
1650 self.message = "was squished";
1651 if(self.dmg && (!self.message2))
1652 self.message2 = "was squished by";
1654 if (self.sounds > 0)
1656 precache_sound ("plats/medplat1.wav");
1657 precache_sound ("plats/medplat2.wav");
1658 self.noise2 = "plats/medplat1.wav";
1659 self.noise1 = "plats/medplat2.wav";
1666 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1668 self.pos1 = '0 0 0';
1669 self.pos2 = self.movedir;
1671 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1672 // but spawn in the open position
1673 if (self.spawnflags & DOOR_START_OPEN)
1674 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1676 self.state = STATE_BOTTOM;
1680 self.takedamage = DAMAGE_YES;
1681 self.event_damage = door_damage;
1687 self.touch = door_touch;
1689 // LinkDoors can't be done until all of the doors have been spawned, so
1690 // the sizes can be detected properly.
1691 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1693 self.reset = door_rotating_reset;
1697 =============================================================================
1701 =============================================================================
1704 void() fd_secret_move1;
1705 void() fd_secret_move2;
1706 void() fd_secret_move3;
1707 void() fd_secret_move4;
1708 void() fd_secret_move5;
1709 void() fd_secret_move6;
1710 void() fd_secret_done;
1712 float SECRET_OPEN_ONCE = 1; // stays open
1713 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1714 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1715 float SECRET_NO_SHOOT = 8; // only opened by trigger
1716 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1719 void fd_secret_use()
1722 string message_save;
1724 self.health = 10000;
1725 self.bot_attack = TRUE;
1727 // exit if still moving around...
1728 if (self.origin != self.oldorigin)
1731 message_save = self.message;
1732 self.message = ""; // no more message
1733 SUB_UseTargets(); // fire all targets / killtargets
1734 self.message = message_save;
1736 self.velocity = '0 0 0';
1738 // Make a sound, wait a little...
1740 if (self.noise1 != "")
1741 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1742 self.nextthink = self.ltime + 0.1;
1744 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1745 makevectors(self.mangle);
1749 if (self.spawnflags & SECRET_1ST_DOWN)
1750 self.t_width = fabs(v_up * self.size);
1752 self.t_width = fabs(v_right * self.size);
1756 self.t_length = fabs(v_forward * self.size);
1758 if (self.spawnflags & SECRET_1ST_DOWN)
1759 self.dest1 = self.origin - v_up * self.t_width;
1761 self.dest1 = self.origin + v_right * (self.t_width * temp);
1763 self.dest2 = self.dest1 + v_forward * self.t_length;
1764 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1765 if (self.noise2 != "")
1766 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1769 // Wait after first movement...
1770 void fd_secret_move1()
1772 self.nextthink = self.ltime + 1.0;
1773 self.think = fd_secret_move2;
1774 if (self.noise3 != "")
1775 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1778 // Start moving sideways w/sound...
1779 void fd_secret_move2()
1781 if (self.noise2 != "")
1782 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1783 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1786 // Wait here until time to go back...
1787 void fd_secret_move3()
1789 if (self.noise3 != "")
1790 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1791 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1793 self.nextthink = self.ltime + self.wait;
1794 self.think = fd_secret_move4;
1799 void fd_secret_move4()
1801 if (self.noise2 != "")
1802 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1803 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1807 void fd_secret_move5()
1809 self.nextthink = self.ltime + 1.0;
1810 self.think = fd_secret_move6;
1811 if (self.noise3 != "")
1812 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1815 void fd_secret_move6()
1817 if (self.noise2 != "")
1818 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1819 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1822 void fd_secret_done()
1824 if (self.spawnflags&SECRET_YES_SHOOT)
1826 self.health = 10000;
1827 self.takedamage = DAMAGE_YES;
1828 //self.th_pain = fd_secret_use;
1830 if (self.noise3 != "")
1831 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1834 void secret_blocked()
1836 if (time < self.attack_finished_single)
1838 self.attack_finished_single = time + 0.5;
1839 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1851 if not(other.iscreature)
1853 if (self.attack_finished_single > time)
1856 self.attack_finished_single = time + 2;
1860 if (other.flags & FL_CLIENT)
1861 centerprint (other, self.message);
1862 play2(other, "misc/talk.wav");
1868 if (self.spawnflags&SECRET_YES_SHOOT)
1870 self.health = 10000;
1871 self.takedamage = DAMAGE_YES;
1873 setorigin(self, self.oldorigin);
1874 self.think = SUB_Null;
1877 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1878 Basic secret door. Slides back, then to the side. Angle determines direction.
1879 wait = # of seconds before coming back
1880 1st_left = 1st move is left of arrow
1881 1st_down = 1st move is down from arrow
1882 always_shoot = even if targeted, keep shootable
1883 t_width = override WIDTH to move back (or height if going down)
1884 t_length = override LENGTH to move sideways
1885 "dmg" damage to inflict when blocked (2 default)
1887 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1894 void spawnfunc_func_door_secret()
1896 /*if (!self.deathtype) // map makers can override this
1897 self.deathtype = " got in the way";*/
1903 self.mangle = self.angles;
1904 self.angles = '0 0 0';
1905 self.classname = "door";
1906 if not(InitMovingBrushTrigger())
1908 self.effects |= EF_LOWPRECISION;
1910 self.touch = secret_touch;
1911 self.blocked = secret_blocked;
1913 self.use = fd_secret_use;
1918 self.spawnflags |= SECRET_YES_SHOOT;
1920 if(self.spawnflags&SECRET_YES_SHOOT)
1922 self.health = 10000;
1923 self.takedamage = DAMAGE_YES;
1924 self.event_damage = fd_secret_use;
1926 self.oldorigin = self.origin;
1928 self.wait = 5; // 5 seconds before closing
1930 self.reset = secret_reset;
1934 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1935 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1936 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
1937 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1938 height: amplitude modifier (default 32)
1939 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1940 noise: path/name of looping .wav file to play.
1941 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1945 void func_fourier_controller_think()
1950 self.nextthink = time + 0.1;
1951 if not (self.owner.active == ACTIVE_ACTIVE)
1953 self.owner.velocity = '0 0 0';
1958 n = floor((tokenize_console(self.owner.netname)) / 5);
1959 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1961 v = self.owner.destvec;
1963 for(i = 0; i < n; ++i)
1965 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1966 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;
1969 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1970 // * 10 so it will arrive in 0.1 sec
1971 self.owner.velocity = (v - self.owner.origin) * 10;
1974 void spawnfunc_func_fourier()
1977 if (self.noise != "")
1979 precache_sound(self.noise);
1980 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1987 self.destvec = self.origin;
1988 self.cnt = 360 / self.speed;
1990 self.blocked = generic_plat_blocked;
1991 if(self.dmg & (!self.message))
1992 self.message = " was squished";
1993 if(self.dmg && (!self.message2))
1994 self.message2 = "was squished by";
1995 if(self.dmg && (!self.dmgtime))
1996 self.dmgtime = 0.25;
1997 self.dmgtime2 = time;
1999 if(self.netname == "")
2000 self.netname = "1 0 0 0 1";
2002 if not(InitMovingBrushTrigger())
2005 self.active = ACTIVE_ACTIVE;
2007 // wait for targets to spawn
2008 controller = spawn();
2009 controller.classname = "func_fourier_controller";
2010 controller.owner = self;
2011 controller.nextthink = time + 1;
2012 controller.think = func_fourier_controller_think;
2013 self.nextthink = self.ltime + 999999999;
2014 self.think = SUB_Null;
2016 // Savage: Reduce bandwith, critical on e.g. nexdm02
2017 self.effects |= EF_LOWPRECISION;
2019 // TODO make a reset function for this one
2022 // reusing some fields havocbots declared
2023 .entity wp00, wp01, wp02, wp03;
2025 .float targetfactor, target2factor, target3factor, target4factor;
2026 .vector targetnormal, target2normal, target3normal, target4normal;
2028 vector func_vectormamamam_origin(entity o, float t)
2040 p = e.origin + t * e.velocity;
2042 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
2044 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
2050 p = e.origin + t * e.velocity;
2052 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
2054 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
2060 p = e.origin + t * e.velocity;
2062 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
2064 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
2070 p = e.origin + t * e.velocity;
2072 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
2074 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2080 void func_vectormamamam_controller_think()
2082 self.nextthink = time + 0.1;
2084 if not (self.owner.active == ACTIVE_ACTIVE)
2086 self.owner.velocity = '0 0 0';
2090 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2091 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2094 void func_vectormamamam_findtarget()
2096 if(self.target != "")
2097 self.wp00 = find(world, targetname, self.target);
2099 if(self.target2 != "")
2100 self.wp01 = find(world, targetname, self.target2);
2102 if(self.target3 != "")
2103 self.wp02 = find(world, targetname, self.target3);
2105 if(self.target4 != "")
2106 self.wp03 = find(world, targetname, self.target4);
2108 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2109 objerror("No reference entity found, so there is nothing to move. Aborting.");
2111 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2114 controller = spawn();
2115 controller.classname = "func_vectormamamam_controller";
2116 controller.owner = self;
2117 controller.nextthink = time + 1;
2118 controller.think = func_vectormamamam_controller_think;
2121 void spawnfunc_func_vectormamamam()
2123 if (self.noise != "")
2125 precache_sound(self.noise);
2126 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2129 if(!self.targetfactor)
2130 self.targetfactor = 1;
2132 if(!self.target2factor)
2133 self.target2factor = 1;
2135 if(!self.target3factor)
2136 self.target3factor = 1;
2138 if(!self.target4factor)
2139 self.target4factor = 1;
2141 if(vlen(self.targetnormal))
2142 self.targetnormal = normalize(self.targetnormal);
2144 if(vlen(self.target2normal))
2145 self.target2normal = normalize(self.target2normal);
2147 if(vlen(self.target3normal))
2148 self.target3normal = normalize(self.target3normal);
2150 if(vlen(self.target4normal))
2151 self.target4normal = normalize(self.target4normal);
2153 self.blocked = generic_plat_blocked;
2154 if(self.dmg & (!self.message))
2155 self.message = " was squished";
2156 if(self.dmg && (!self.message2))
2157 self.message2 = "was squished by";
2158 if(self.dmg && (!self.dmgtime))
2159 self.dmgtime = 0.25;
2160 self.dmgtime2 = time;
2162 if(self.netname == "")
2163 self.netname = "1 0 0 0 1";
2165 if not(InitMovingBrushTrigger())
2168 // wait for targets to spawn
2169 self.nextthink = self.ltime + 999999999;
2170 self.think = SUB_Null;
2172 // Savage: Reduce bandwith, critical on e.g. nexdm02
2173 self.effects |= EF_LOWPRECISION;
2175 self.active = ACTIVE_ACTIVE;
2177 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2180 void conveyor_think()
2184 // set myself as current conveyor where possible
2185 for(e = world; (e = findentity(e, conveyor, self)); )
2190 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2191 if(!e.conveyor.state)
2194 vector emin = e.absmin;
2195 vector emax = e.absmax;
2196 if(self.solid == SOLID_BSP)
2201 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2202 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2206 for(e = world; (e = findentity(e, conveyor, self)); )
2208 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2209 continue; // done in SV_PlayerPhysics
2211 setorigin(e, e.origin + self.movedir * sys_frametime);
2212 move_out_of_solid(e);
2213 UpdateCSQCProjectile(e);
2215 // stupid conveyor code
2216 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2217 if(trace_fraction > 0)
2218 setorigin(e, trace_endpos);
2223 self.nextthink = time;
2228 self.state = !self.state;
2231 void conveyor_reset()
2233 self.state = (self.spawnflags & 1);
2236 void conveyor_init()
2240 self.movedir = self.movedir * self.speed;
2241 self.think = conveyor_think;
2242 self.nextthink = time;
2245 self.use = conveyor_use;
2246 self.reset = conveyor_reset;
2253 void spawnfunc_trigger_conveyor()
2260 void spawnfunc_func_conveyor()
2263 InitMovingBrushTrigger();
2264 self.movetype = MOVETYPE_NONE;