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');
17 .entity trigger_field;
19 void() plat_center_touch;
20 void() plat_outside_touch;
21 void() plat_trigger_use;
25 const float PLAT_LOW_TRIGGER = 1;
27 void plat_spawn_inside_trigger()
33 trigger.touch = plat_center_touch;
34 trigger.movetype = MOVETYPE_NONE;
35 trigger.solid = SOLID_TRIGGER;
38 tmin = self.absmin + '25 25 0';
39 tmax = self.absmax - '25 25 -8';
40 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
41 if (self.spawnflags & PLAT_LOW_TRIGGER)
44 if (self.size_x <= 50)
46 tmin_x = (self.mins_x + self.maxs_x) / 2;
49 if (self.size_y <= 50)
51 tmin_y = (self.mins_y + self.maxs_y) / 2;
59 setsize (trigger, tmin, tmax);
63 // otherwise, something is fishy...
65 objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
70 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
72 self.think = plat_go_down;
73 self.nextthink = self.ltime + 3;
76 void plat_hit_bottom()
78 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
84 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
86 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
91 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
93 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
96 void plat_center_touch()
98 if not(other.iscreature)
101 if (other.health <= 0)
107 else if (self.state == 1)
108 self.nextthink = self.ltime + 1; // delay going down
111 void plat_outside_touch()
113 if not(other.iscreature)
116 if (other.health <= 0)
124 void plat_trigger_use()
127 return; // already activated
134 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
135 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
137 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
138 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
139 // Gib dead/dying stuff
140 if(other.deadflag != DEAD_NO)
141 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
146 else if (self.state == 3)
148 // when in other states, then the plat_crush event came delayed after
149 // plat state already had changed
150 // this isn't a bug per se!
156 self.use = func_null;
158 objerror ("plat_use: not in up state");
162 .string sound1, sound2;
168 setorigin (self, self.pos1);
174 setorigin (self, self.pos2);
176 self.use = plat_trigger_use;
180 .float platmovetype_start_default, platmovetype_end_default;
181 float set_platmovetype(entity e, string s)
183 // sets platmovetype_start and platmovetype_end based on a string consisting of two values
186 n = tokenize_console(s);
188 e.platmovetype_start = stof(argv(0));
190 e.platmovetype_start = 0;
193 e.platmovetype_end = stof(argv(1));
195 e.platmovetype_end = e.platmovetype_start;
198 if(argv(2) == "force")
199 return TRUE; // no checking, return immediately
201 if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
203 objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
210 void spawnfunc_path_corner()
212 // setup values for overriding train movement
213 // if a second value does not exist, both start and end speeds are the single value specified
214 if(!set_platmovetype(self, self.platmovetype))
217 void spawnfunc_func_plat()
219 if (self.sounds == 0)
222 if(self.spawnflags & 4)
225 if(self.dmg && (self.message == ""))
226 self.message = "was squished";
227 if(self.dmg && (self.message2 == ""))
228 self.message2 = "was squished by";
230 if (self.sounds == 1)
232 precache_sound ("plats/plat1.wav");
233 precache_sound ("plats/plat2.wav");
234 self.noise = "plats/plat1.wav";
235 self.noise1 = "plats/plat2.wav";
238 if (self.sounds == 2)
240 precache_sound ("plats/medplat1.wav");
241 precache_sound ("plats/medplat2.wav");
242 self.noise = "plats/medplat1.wav";
243 self.noise1 = "plats/medplat2.wav";
248 precache_sound (self.sound1);
249 self.noise = self.sound1;
253 precache_sound (self.sound2);
254 self.noise1 = self.sound2;
257 self.mangle = self.angles;
258 self.angles = '0 0 0';
260 self.classname = "plat";
261 if not(InitMovingBrushTrigger())
263 self.effects |= EF_LOWPRECISION;
264 setsize (self, self.mins , self.maxs);
266 self.blocked = plat_crush;
273 self.height = self.size_z - self.lip;
275 self.pos1 = self.origin;
276 self.pos2 = self.origin;
277 self.pos2_z = self.origin_z - self.height;
279 self.reset = plat_reset;
282 plat_spawn_inside_trigger (); // the "start moving" trigger
285 .float train_wait_turning;
296 // if turning is enabled, the train will turn toward the next point while waiting
297 if(self.platmovetype_turn && !self.train_wait_turning)
301 targ = find(world, targetname, self.target);
302 if((self.spawnflags & 1) && targ.curvetarget)
303 cp = find(world, targetname, targ.curvetarget);
307 if(cp) // bezier curves movement
308 ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
309 else // linear movement
310 ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
311 ang = vectoangles(ang);
312 ang_x = -ang_x; // flip up / down orientation
314 if(self.wait > 0) // slow turning
315 SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
316 else // instant turning
317 SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait);
318 self.train_wait_turning = TRUE;
323 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
325 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
327 self.train_wait_turning = FALSE;
332 self.think = train_next;
333 self.nextthink = self.ltime + self.wait;
342 targ = find(world, targetname, self.target);
343 self.target = targ.target;
344 if (self.spawnflags & 1)
348 cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
349 cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
354 if (self.target == "")
355 objerror("train_next: no next target");
356 self.wait = targ.wait;
360 if(targ.platmovetype)
362 // this path_corner contains a movetype overrider, apply it
363 self.platmovetype_start = targ.platmovetype_start;
364 self.platmovetype_end = targ.platmovetype_end;
368 // this path_corner doesn't contain a movetype overrider, use the train's defaults
369 self.platmovetype_start = self.platmovetype_start_default;
370 self.platmovetype_end = self.platmovetype_end_default;
376 SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
378 SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
383 SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
385 SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
389 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
392 void func_train_find()
395 targ = find(world, targetname, self.target);
396 self.target = targ.target;
397 if (self.target == "")
398 objerror("func_train_find: no next target");
399 setorigin(self, targ.origin - self.view_ofs);
400 self.nextthink = self.ltime + 1;
401 self.think = train_next;
404 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
405 Ridable platform, targets spawnfunc_path_corner path to follow.
406 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
407 target : targetname of first spawnfunc_path_corner (starts here)
409 void spawnfunc_func_train()
411 if (self.noise != "")
412 precache_sound(self.noise);
414 if (self.target == "")
415 objerror("func_train without a target");
419 if (self.spawnflags & 2)
421 self.platmovetype_turn = TRUE;
422 self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
425 self.view_ofs = self.mins;
427 if not(InitMovingBrushTrigger())
429 self.effects |= EF_LOWPRECISION;
431 // wait for targets to spawn
432 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
434 self.blocked = generic_plat_blocked;
435 if(self.dmg && (self.message == ""))
436 self.message = " was squished";
437 if(self.dmg && (self.message2 == ""))
438 self.message2 = "was squished by";
439 if(self.dmg && (!self.dmgtime))
441 self.dmgtime2 = time;
443 if(!set_platmovetype(self, self.platmovetype))
445 self.platmovetype_start_default = self.platmovetype_start;
446 self.platmovetype_end_default = self.platmovetype_end;
448 // TODO make a reset function for this one
451 void func_rotating_setactive(float astate)
454 if (astate == ACTIVE_TOGGLE)
456 if(self.active == ACTIVE_ACTIVE)
457 self.active = ACTIVE_NOT;
459 self.active = ACTIVE_ACTIVE;
462 self.active = astate;
464 if(self.active == ACTIVE_NOT)
465 self.avelocity = '0 0 0';
467 self.avelocity = self.pos1;
470 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
471 Brush model that spins in place on one axis (default Z).
472 speed : speed to rotate (in degrees per second)
473 noise : path/name of looping .wav file to play.
474 dmg : Do this mutch dmg every .dmgtime intervall when blocked
478 void spawnfunc_func_rotating()
480 if (self.noise != "")
482 precache_sound(self.noise);
483 ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE);
486 self.active = ACTIVE_ACTIVE;
487 self.setactive = func_rotating_setactive;
491 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
492 if (self.spawnflags & 4) // X (untested)
493 self.avelocity = '0 0 1' * self.speed;
494 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
495 else if (self.spawnflags & 8) // Y (untested)
496 self.avelocity = '1 0 0' * self.speed;
497 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
499 self.avelocity = '0 1 0' * self.speed;
501 self.pos1 = self.avelocity;
503 if(self.dmg && (self.message == ""))
504 self.message = " was squished";
505 if(self.dmg && (self.message2 == ""))
506 self.message2 = "was squished by";
509 if(self.dmg && (!self.dmgtime))
512 self.dmgtime2 = time;
514 if not(InitMovingBrushTrigger())
516 // no EF_LOWPRECISION here, as rounding angles is bad
518 self.blocked = generic_plat_blocked;
520 // wait for targets to spawn
521 self.nextthink = self.ltime + 999999999;
522 self.think = SUB_NullThink; // for PushMove
524 // TODO make a reset function for this one
528 void func_bobbing_controller_think()
531 self.nextthink = time + 0.1;
533 if not (self.owner.active == ACTIVE_ACTIVE)
535 self.owner.velocity = '0 0 0';
539 // calculate sinewave using makevectors
540 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
541 v = self.owner.destvec + self.owner.movedir * v_forward_y;
542 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
543 // * 10 so it will arrive in 0.1 sec
544 self.owner.velocity = (v - self.owner.origin) * 10;
547 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
548 Brush model that moves back and forth on one axis (default Z).
549 speed : how long one cycle takes in seconds (default 4)
550 height : how far the cycle moves (default 32)
551 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
552 noise : path/name of looping .wav file to play.
553 dmg : Do this mutch dmg every .dmgtime intervall when blocked
556 void spawnfunc_func_bobbing()
559 if (self.noise != "")
561 precache_sound(self.noise);
562 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
568 // center of bobbing motion
569 self.destvec = self.origin;
570 // time scale to get degrees
571 self.cnt = 360 / self.speed;
573 self.active = ACTIVE_ACTIVE;
575 // damage when blocked
576 self.blocked = generic_plat_blocked;
577 if(self.dmg && (self.message == ""))
578 self.message = " was squished";
579 if(self.dmg && (self.message2 == ""))
580 self.message2 = "was squished by";
581 if(self.dmg && (!self.dmgtime))
583 self.dmgtime2 = time;
586 if (self.spawnflags & 1) // X
587 self.movedir = '1 0 0' * self.height;
588 else if (self.spawnflags & 2) // Y
589 self.movedir = '0 1 0' * self.height;
591 self.movedir = '0 0 1' * self.height;
593 if not(InitMovingBrushTrigger())
596 // wait for targets to spawn
597 controller = spawn();
598 controller.classname = "func_bobbing_controller";
599 controller.owner = self;
600 controller.nextthink = time + 1;
601 controller.think = func_bobbing_controller_think;
602 self.nextthink = self.ltime + 999999999;
603 self.think = SUB_NullThink; // for PushMove
605 // Savage: Reduce bandwith, critical on e.g. nexdm02
606 self.effects |= EF_LOWPRECISION;
608 // TODO make a reset function for this one
612 void func_pendulum_controller_think()
615 self.nextthink = time + 0.1;
617 if not (self.owner.active == ACTIVE_ACTIVE)
619 self.owner.avelocity_x = 0;
623 // calculate sinewave using makevectors
624 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
625 v = self.owner.speed * v_forward_y + self.cnt;
626 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
628 // * 10 so it will arrive in 0.1 sec
629 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
633 void spawnfunc_func_pendulum()
636 if (self.noise != "")
638 precache_sound(self.noise);
639 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
642 self.active = ACTIVE_ACTIVE;
644 // keys: angle, speed, phase, noise, freq
648 // not initializing self.dmg to 2, to allow damageless pendulum
650 if(self.dmg && (self.message == ""))
651 self.message = " was squished";
652 if(self.dmg && (self.message2 == ""))
653 self.message2 = "was squished by";
654 if(self.dmg && (!self.dmgtime))
656 self.dmgtime2 = time;
658 self.blocked = generic_plat_blocked;
660 self.avelocity_z = 0.0000001;
661 if not(InitMovingBrushTrigger())
666 // find pendulum length (same formula as Q3A)
667 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
670 // copy initial angle
671 self.cnt = self.angles_z;
673 // wait for targets to spawn
674 controller = spawn();
675 controller.classname = "func_pendulum_controller";
676 controller.owner = self;
677 controller.nextthink = time + 1;
678 controller.think = func_pendulum_controller_think;
679 self.nextthink = self.ltime + 999999999;
680 self.think = SUB_NullThink; // for PushMove
682 //self.effects |= EF_LOWPRECISION;
684 // TODO make a reset function for this one
687 // button and multiple button
690 void() button_return;
694 self.state = STATE_TOP;
695 self.nextthink = self.ltime + self.wait;
696 self.think = button_return;
697 activator = self.enemy;
699 self.frame = 1; // use alternate textures
704 self.state = STATE_BOTTOM;
709 self.state = STATE_DOWN;
710 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
711 self.frame = 0; // use normal textures
713 self.takedamage = DAMAGE_YES; // can be shot again
717 void button_blocked()
719 // do nothing, just don't come all the way back out
725 self.health = self.max_health;
726 self.takedamage = DAMAGE_NO; // will be reset upon return
728 if (self.state == STATE_UP || self.state == STATE_TOP)
731 if (self.noise != "")
732 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
734 self.state = STATE_UP;
735 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
740 self.health = self.max_health;
741 setorigin(self, self.pos1);
742 self.frame = 0; // use normal textures
743 self.state = STATE_BOTTOM;
745 self.takedamage = DAMAGE_YES; // can be shot again
750 if not (self.active == ACTIVE_ACTIVE)
753 self.enemy = activator;
761 if not(other.iscreature)
763 if(other.velocity * self.movedir < 0)
767 self.enemy = other.owner;
771 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
773 if(self.spawnflags & DOOR_NOSPLASH)
774 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
776 self.health = self.health - damage;
777 if (self.health <= 0)
779 self.enemy = damage_attacker;
785 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
786 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.
788 "angle" determines the opening direction
789 "target" all entities with a matching targetname will be used
790 "speed" override the default 40 speed
791 "wait" override the default 1 second wait (-1 = never return)
792 "lip" override the default 4 pixel lip remaining at end of move
793 "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
800 void spawnfunc_func_button()
804 if not(InitMovingBrushTrigger())
806 self.effects |= EF_LOWPRECISION;
808 self.blocked = button_blocked;
809 self.use = button_use;
811 // if (self.health == 0) // all buttons are now shootable
815 self.max_health = self.health;
816 self.event_damage = button_damage;
817 self.takedamage = DAMAGE_YES;
820 self.touch = button_touch;
830 precache_sound(self.noise);
832 self.active = ACTIVE_ACTIVE;
834 self.pos1 = self.origin;
835 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
836 self.flags |= FL_NOTARGET;
842 const float DOOR_START_OPEN = 1;
843 const float DOOR_DONT_LINK = 4;
844 const float DOOR_TOGGLE = 32;
848 Doors are similar to buttons, but can spawn a fat trigger field around them
849 to open without a touch, and they link together to form simultanious
852 Door.owner is the master door. If there is only one door, it points to itself.
853 If multiple doors, all will point to a single one.
855 Door.enemy chains from the master door through all doors linked in the chain.
860 =============================================================================
864 =============================================================================
869 void() door_rotating_go_down;
870 void() door_rotating_go_up;
875 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
876 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
879 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
880 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
882 //Dont chamge direction for dead or dying stuff
883 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
886 if (self.state == STATE_DOWN)
887 if (self.classname == "door")
892 door_rotating_go_up ();
895 if (self.classname == "door")
900 door_rotating_go_down ();
904 //gib dying stuff just to make sure
905 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
906 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
910 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
911 // if a door has a negative wait, it would never come back if blocked,
912 // so let it just squash the object to death real fast
913 /* if (self.wait >= 0)
915 if (self.state == STATE_DOWN)
926 if (self.noise1 != "")
927 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
928 self.state = STATE_TOP;
929 if (self.spawnflags & DOOR_TOGGLE)
930 return; // don't come down automatically
931 if (self.classname == "door")
933 self.think = door_go_down;
936 self.think = door_rotating_go_down;
938 self.nextthink = self.ltime + self.wait;
941 void door_hit_bottom()
943 if (self.noise1 != "")
944 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
945 self.state = STATE_BOTTOM;
950 if (self.noise2 != "")
951 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
954 self.takedamage = DAMAGE_YES;
955 self.health = self.max_health;
958 self.state = STATE_DOWN;
959 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
964 if (self.state == STATE_UP)
965 return; // already going up
967 if (self.state == STATE_TOP)
968 { // reset top wait time
969 self.nextthink = self.ltime + self.wait;
973 if (self.noise2 != "")
974 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
975 self.state = STATE_UP;
976 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
979 oldmessage = self.message;
982 self.message = oldmessage;
988 =============================================================================
992 =============================================================================
995 float door_check_keys(void) {
1005 if not(door.itemkeys)
1008 // this door require a key
1009 // only a player can have a key
1010 if not(IS_PLAYER(other))
1013 if (item_keys_usekey(door, other)) {
1014 // some keys were used
1015 if (other.key_door_messagetime <= time) {
1016 play2(other, "misc/talk.wav");
1017 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
1018 other.key_door_messagetime = time + 2;
1021 // no keys were used
1022 if (other.key_door_messagetime <= time) {
1023 play2(other, "misc/talk.wav");
1024 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
1025 other.key_door_messagetime = time + 2;
1029 if (door.itemkeys) {
1030 // door is now unlocked
1031 play2(other, "misc/talk.wav");
1032 centerprint(other, "Door unlocked!");
1044 if (self.owner != self)
1045 objerror ("door_fire: self.owner != self");
1049 if (self.spawnflags & DOOR_TOGGLE)
1051 if (self.state == STATE_UP || self.state == STATE_TOP)
1056 if (self.classname == "door")
1062 door_rotating_go_down ();
1065 } while ( (self != starte) && (self != world) );
1071 // trigger all paired doors
1075 if (self.classname == "door")
1080 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
1081 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
1083 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
1084 self.pos2 = '0 0 0' - self.pos2;
1086 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
1087 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
1088 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1090 door_rotating_go_up ();
1094 } while ( (self != starte) && (self != world) );
1103 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1115 void door_trigger_touch()
1117 if (other.health < 1)
1118 if not(other.iscreature && other.deadflag == DEAD_NO)
1121 if (time < self.attack_finished_single)
1124 // check if door is locked
1125 if (!door_check_keys())
1128 self.attack_finished_single = time + 1;
1137 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1140 if(self.spawnflags & DOOR_NOSPLASH)
1141 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1143 self.health = self.health - damage;
1145 if (self.itemkeys) {
1146 // don't allow opening doors through damage if keys are required
1150 if (self.health <= 0)
1154 self.health = self.max_health;
1155 self.takedamage = DAMAGE_NO; // wil be reset upon return
1171 if not(IS_PLAYER(other))
1173 if (self.owner.attack_finished_single > time)
1176 self.owner.attack_finished_single = time + 2;
1178 if (!(self.owner.dmg) && (self.owner.message != ""))
1180 if (IS_CLIENT(other))
1181 centerprint (other, self.owner.message);
1182 play2(other, "misc/talk.wav");
1187 void door_generic_plat_blocked()
1190 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1191 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1194 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1195 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1197 //Dont chamge direction for dead or dying stuff
1198 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1201 if (self.state == STATE_DOWN)
1202 door_rotating_go_up ();
1204 door_rotating_go_down ();
1207 //gib dying stuff just to make sure
1208 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1209 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1213 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1214 // if a door has a negative wait, it would never come back if blocked,
1215 // so let it just squash the object to death real fast
1216 /* if (self.wait >= 0)
1218 if (self.state == STATE_DOWN)
1219 door_rotating_go_up ();
1221 door_rotating_go_down ();
1227 void door_rotating_hit_top()
1229 if (self.noise1 != "")
1230 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1231 self.state = STATE_TOP;
1232 if (self.spawnflags & DOOR_TOGGLE)
1233 return; // don't come down automatically
1234 self.think = door_rotating_go_down;
1235 self.nextthink = self.ltime + self.wait;
1238 void door_rotating_hit_bottom()
1240 if (self.noise1 != "")
1241 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1242 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1244 self.pos2 = '0 0 0' - self.pos2;
1247 self.state = STATE_BOTTOM;
1250 void door_rotating_go_down()
1252 if (self.noise2 != "")
1253 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1254 if (self.max_health)
1256 self.takedamage = DAMAGE_YES;
1257 self.health = self.max_health;
1260 self.state = STATE_DOWN;
1261 SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
1264 void door_rotating_go_up()
1266 if (self.state == STATE_UP)
1267 return; // already going up
1269 if (self.state == STATE_TOP)
1270 { // reset top wait time
1271 self.nextthink = self.ltime + self.wait;
1274 if (self.noise2 != "")
1275 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1276 self.state = STATE_UP;
1277 SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
1280 oldmessage = self.message;
1283 self.message = oldmessage;
1290 =============================================================================
1294 =============================================================================
1298 entity spawn_field(vector fmins, vector fmaxs)
1304 trigger.classname = "doortriggerfield";
1305 trigger.movetype = MOVETYPE_NONE;
1306 trigger.solid = SOLID_TRIGGER;
1307 trigger.owner = self;
1308 trigger.touch = door_trigger_touch;
1312 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1317 entity LinkDoors_nextent(entity cur, entity near, entity pass)
1319 while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
1325 float LinkDoors_isconnected(entity e1, entity e2, entity pass)
1328 if (e1.absmin_x > e2.absmax_x + DELTA)
1330 if (e1.absmin_y > e2.absmax_y + DELTA)
1332 if (e1.absmin_z > e2.absmax_z + DELTA)
1334 if (e2.absmin_x > e1.absmax_x + DELTA)
1336 if (e2.absmin_y > e1.absmax_y + DELTA)
1338 if (e2.absmin_z > e1.absmax_z + DELTA)
1353 vector cmins, cmaxs;
1356 return; // already linked by another door
1357 if (self.spawnflags & 4)
1359 self.owner = self.enemy = self;
1367 self.trigger_field = spawn_field(self.absmin, self.absmax);
1369 return; // don't want to link this door
1372 FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
1374 // set owner, and make a loop of the chain
1375 dprint("LinkDoors: linking doors:");
1376 for(t = self; ; t = t.enemy)
1378 dprint(" ", etos(t));
1380 if(t.enemy == world)
1388 // collect health, targetname, message, size
1389 cmins = self.absmin;
1390 cmaxs = self.absmax;
1391 for(t = self; ; t = t.enemy)
1393 if(t.health && !self.health)
1394 self.health = t.health;
1395 if((t.targetname != "") && (self.targetname == ""))
1396 self.targetname = t.targetname;
1397 if((t.message != "") && (self.message == ""))
1398 self.message = t.message;
1399 if (t.absmin_x < cmins_x)
1400 cmins_x = t.absmin_x;
1401 if (t.absmin_y < cmins_y)
1402 cmins_y = t.absmin_y;
1403 if (t.absmin_z < cmins_z)
1404 cmins_z = t.absmin_z;
1405 if (t.absmax_x > cmaxs_x)
1406 cmaxs_x = t.absmax_x;
1407 if (t.absmax_y > cmaxs_y)
1408 cmaxs_y = t.absmax_y;
1409 if (t.absmax_z > cmaxs_z)
1410 cmaxs_z = t.absmax_z;
1415 // distribute health, targetname, message
1416 for(t = self; t; t = t.enemy)
1418 t.health = self.health;
1419 t.targetname = self.targetname;
1420 t.message = self.message;
1425 // shootable, or triggered doors just needed the owner/enemy links,
1426 // they don't spawn a field
1435 self.trigger_field = spawn_field(cmins, cmaxs);
1439 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1440 if two doors touch, they are assumed to be connected and operate as a unit.
1442 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1444 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).
1446 GOLD_KEY causes the door to open only if the activator holds a gold key.
1448 SILVER_KEY causes the door to open only if the activator holds a silver key.
1450 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1451 "angle" determines the opening direction
1452 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1453 "health" if set, door must be shot open
1454 "speed" movement speed (100 default)
1455 "wait" wait before returning (3 default, -1 = never return)
1456 "lip" lip remaining at end of move (8 default)
1457 "dmg" damage to inflict when blocked (2 default)
1464 FIXME: only one sound set available at the time being
1468 void door_init_startopen()
1470 setorigin (self, self.pos2);
1471 self.pos2 = self.pos1;
1472 self.pos1 = self.origin;
1477 setorigin(self, self.pos1);
1478 self.velocity = '0 0 0';
1479 self.state = STATE_BOTTOM;
1480 self.think = func_null;
1484 // spawnflags require key (for now only func_door)
1485 #define SPAWNFLAGS_GOLD_KEY 8
1486 #define SPAWNFLAGS_SILVER_KEY 16
1487 void spawnfunc_func_door()
1489 // Quake 1 keys compatibility
1490 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1491 self.itemkeys |= ITEM_KEY_BIT(0);
1492 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1493 self.itemkeys |= ITEM_KEY_BIT(1);
1495 //if (!self.deathtype) // map makers can override this
1496 // self.deathtype = " got in the way";
1499 self.max_health = self.health;
1500 if not(InitMovingBrushTrigger())
1502 self.effects |= EF_LOWPRECISION;
1503 self.classname = "door";
1505 self.blocked = door_blocked;
1506 self.use = door_use;
1508 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1509 // if(self.spawnflags & 8)
1510 // self.dmg = 10000;
1512 if(self.dmg && (self.message == ""))
1513 self.message = "was squished";
1514 if(self.dmg && (self.message2 == ""))
1515 self.message2 = "was squished by";
1517 if (self.sounds > 0)
1519 precache_sound ("plats/medplat1.wav");
1520 precache_sound ("plats/medplat2.wav");
1521 self.noise2 = "plats/medplat1.wav";
1522 self.noise1 = "plats/medplat2.wav";
1532 self.pos1 = self.origin;
1533 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1535 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1536 // but spawn in the open position
1537 if (self.spawnflags & DOOR_START_OPEN)
1538 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1540 self.state = STATE_BOTTOM;
1544 self.takedamage = DAMAGE_YES;
1545 self.event_damage = door_damage;
1551 self.touch = door_touch;
1553 // LinkDoors can't be done until all of the doors have been spawned, so
1554 // the sizes can be detected properly.
1555 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1557 self.reset = door_reset;
1560 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1561 if two doors touch, they are assumed to be connected and operate as a unit.
1563 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1565 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1566 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1567 must have set trigger_reverse to 1.
1568 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1570 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).
1572 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1573 "angle" determines the destination angle for opening. negative values reverse the direction.
1574 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1575 "health" if set, door must be shot open
1576 "speed" movement speed (100 default)
1577 "wait" wait before returning (3 default, -1 = never return)
1578 "dmg" damage to inflict when blocked (2 default)
1585 FIXME: only one sound set available at the time being
1588 void door_rotating_reset()
1590 self.angles = self.pos1;
1591 self.avelocity = '0 0 0';
1592 self.state = STATE_BOTTOM;
1593 self.think = func_null;
1597 void door_rotating_init_startopen()
1599 self.angles = self.movedir;
1600 self.pos2 = '0 0 0';
1601 self.pos1 = self.movedir;
1605 void spawnfunc_func_door_rotating()
1608 //if (!self.deathtype) // map makers can override this
1609 // self.deathtype = " got in the way";
1611 // I abuse "movedir" for denoting the axis for now
1612 if (self.spawnflags & 64) // X (untested)
1613 self.movedir = '0 0 1';
1614 else if (self.spawnflags & 128) // Y (untested)
1615 self.movedir = '1 0 0';
1617 self.movedir = '0 1 0';
1619 if (self.angles_y==0) self.angles_y = 90;
1621 self.movedir = self.movedir * self.angles_y;
1622 self.angles = '0 0 0';
1624 self.max_health = self.health;
1625 self.avelocity = self.movedir;
1626 if not(InitMovingBrushTrigger())
1628 self.velocity = '0 0 0';
1629 //self.effects |= EF_LOWPRECISION;
1630 self.classname = "door_rotating";
1632 self.blocked = door_blocked;
1633 self.use = door_use;
1635 if(self.spawnflags & 8)
1638 if(self.dmg && (self.message == ""))
1639 self.message = "was squished";
1640 if(self.dmg && (self.message2 == ""))
1641 self.message2 = "was squished by";
1643 if (self.sounds > 0)
1645 precache_sound ("plats/medplat1.wav");
1646 precache_sound ("plats/medplat2.wav");
1647 self.noise2 = "plats/medplat1.wav";
1648 self.noise1 = "plats/medplat2.wav";
1655 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1657 self.pos1 = '0 0 0';
1658 self.pos2 = self.movedir;
1660 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1661 // but spawn in the open position
1662 if (self.spawnflags & DOOR_START_OPEN)
1663 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1665 self.state = STATE_BOTTOM;
1669 self.takedamage = DAMAGE_YES;
1670 self.event_damage = door_damage;
1676 self.touch = door_touch;
1678 // LinkDoors can't be done until all of the doors have been spawned, so
1679 // the sizes can be detected properly.
1680 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1682 self.reset = door_rotating_reset;
1686 =============================================================================
1690 =============================================================================
1693 void() fd_secret_move1;
1694 void() fd_secret_move2;
1695 void() fd_secret_move3;
1696 void() fd_secret_move4;
1697 void() fd_secret_move5;
1698 void() fd_secret_move6;
1699 void() fd_secret_done;
1701 const float SECRET_OPEN_ONCE = 1; // stays open
1702 const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1703 const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1704 const float SECRET_NO_SHOOT = 8; // only opened by trigger
1705 const float SECRET_YES_SHOOT = 16; // shootable even if targeted
1707 void fd_secret_use()
1710 string message_save;
1712 self.health = 10000;
1713 self.bot_attack = TRUE;
1715 // exit if still moving around...
1716 if (self.origin != self.oldorigin)
1719 message_save = self.message;
1720 self.message = ""; // no more message
1721 SUB_UseTargets(); // fire all targets / killtargets
1722 self.message = message_save;
1724 self.velocity = '0 0 0';
1726 // Make a sound, wait a little...
1728 if (self.noise1 != "")
1729 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
1730 self.nextthink = self.ltime + 0.1;
1732 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1733 makevectors(self.mangle);
1737 if (self.spawnflags & SECRET_1ST_DOWN)
1738 self.t_width = fabs(v_up * self.size);
1740 self.t_width = fabs(v_right * self.size);
1744 self.t_length = fabs(v_forward * self.size);
1746 if (self.spawnflags & SECRET_1ST_DOWN)
1747 self.dest1 = self.origin - v_up * self.t_width;
1749 self.dest1 = self.origin + v_right * (self.t_width * temp);
1751 self.dest2 = self.dest1 + v_forward * self.t_length;
1752 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1753 if (self.noise2 != "")
1754 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1757 void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1762 // Wait after first movement...
1763 void fd_secret_move1()
1765 self.nextthink = self.ltime + 1.0;
1766 self.think = fd_secret_move2;
1767 if (self.noise3 != "")
1768 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1771 // Start moving sideways w/sound...
1772 void fd_secret_move2()
1774 if (self.noise2 != "")
1775 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1776 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1779 // Wait here until time to go back...
1780 void fd_secret_move3()
1782 if (self.noise3 != "")
1783 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1784 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1786 self.nextthink = self.ltime + self.wait;
1787 self.think = fd_secret_move4;
1792 void fd_secret_move4()
1794 if (self.noise2 != "")
1795 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1796 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1800 void fd_secret_move5()
1802 self.nextthink = self.ltime + 1.0;
1803 self.think = fd_secret_move6;
1804 if (self.noise3 != "")
1805 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1808 void fd_secret_move6()
1810 if (self.noise2 != "")
1811 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
1812 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1815 void fd_secret_done()
1817 if (self.spawnflags&SECRET_YES_SHOOT)
1819 self.health = 10000;
1820 self.takedamage = DAMAGE_YES;
1821 //self.th_pain = fd_secret_use;
1823 if (self.noise3 != "")
1824 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
1827 void secret_blocked()
1829 if (time < self.attack_finished_single)
1831 self.attack_finished_single = time + 0.5;
1832 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1844 if not(other.iscreature)
1846 if (self.attack_finished_single > time)
1849 self.attack_finished_single = time + 2;
1853 if (IS_CLIENT(other))
1854 centerprint (other, self.message);
1855 play2(other, "misc/talk.wav");
1861 if (self.spawnflags&SECRET_YES_SHOOT)
1863 self.health = 10000;
1864 self.takedamage = DAMAGE_YES;
1866 setorigin(self, self.oldorigin);
1867 self.think = func_null;
1871 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1872 Basic secret door. Slides back, then to the side. Angle determines direction.
1873 wait = # of seconds before coming back
1874 1st_left = 1st move is left of arrow
1875 1st_down = 1st move is down from arrow
1876 always_shoot = even if targeted, keep shootable
1877 t_width = override WIDTH to move back (or height if going down)
1878 t_length = override LENGTH to move sideways
1879 "dmg" damage to inflict when blocked (2 default)
1881 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1888 void spawnfunc_func_door_secret()
1890 /*if (!self.deathtype) // map makers can override this
1891 self.deathtype = " got in the way";*/
1897 self.mangle = self.angles;
1898 self.angles = '0 0 0';
1899 self.classname = "door";
1900 if not(InitMovingBrushTrigger())
1902 self.effects |= EF_LOWPRECISION;
1904 self.touch = secret_touch;
1905 self.blocked = secret_blocked;
1907 self.use = fd_secret_use;
1912 self.spawnflags |= SECRET_YES_SHOOT;
1914 if(self.spawnflags&SECRET_YES_SHOOT)
1916 self.health = 10000;
1917 self.takedamage = DAMAGE_YES;
1918 self.event_damage = fd_secret_damage;
1920 self.oldorigin = self.origin;
1922 self.wait = 5; // 5 seconds before closing
1924 self.reset = secret_reset;
1928 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1929 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1930 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
1931 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1932 height: amplitude modifier (default 32)
1933 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1934 noise: path/name of looping .wav file to play.
1935 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1939 void func_fourier_controller_think()
1944 self.nextthink = time + 0.1;
1945 if not (self.owner.active == ACTIVE_ACTIVE)
1947 self.owner.velocity = '0 0 0';
1952 n = floor((tokenize_console(self.owner.netname)) / 5);
1953 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1955 v = self.owner.destvec;
1957 for(i = 0; i < n; ++i)
1959 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1960 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;
1963 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1964 // * 10 so it will arrive in 0.1 sec
1965 self.owner.velocity = (v - self.owner.origin) * 10;
1968 void spawnfunc_func_fourier()
1971 if (self.noise != "")
1973 precache_sound(self.noise);
1974 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
1981 self.destvec = self.origin;
1982 self.cnt = 360 / self.speed;
1984 self.blocked = generic_plat_blocked;
1985 if(self.dmg && (self.message == ""))
1986 self.message = " was squished";
1987 if(self.dmg && (self.message2 == ""))
1988 self.message2 = "was squished by";
1989 if(self.dmg && (!self.dmgtime))
1990 self.dmgtime = 0.25;
1991 self.dmgtime2 = time;
1993 if(self.netname == "")
1994 self.netname = "1 0 0 0 1";
1996 if not(InitMovingBrushTrigger())
1999 self.active = ACTIVE_ACTIVE;
2001 // wait for targets to spawn
2002 controller = spawn();
2003 controller.classname = "func_fourier_controller";
2004 controller.owner = self;
2005 controller.nextthink = time + 1;
2006 controller.think = func_fourier_controller_think;
2007 self.nextthink = self.ltime + 999999999;
2008 self.think = SUB_NullThink; // for PushMove
2010 // Savage: Reduce bandwith, critical on e.g. nexdm02
2011 self.effects |= EF_LOWPRECISION;
2013 // TODO make a reset function for this one
2016 // reusing some fields havocbots declared
2017 .entity wp00, wp01, wp02, wp03;
2019 .float targetfactor, target2factor, target3factor, target4factor;
2020 .vector targetnormal, target2normal, target3normal, target4normal;
2022 vector func_vectormamamam_origin(entity o, float t)
2034 p = e.origin + t * e.velocity;
2036 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
2038 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
2044 p = e.origin + t * e.velocity;
2046 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
2048 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
2054 p = e.origin + t * e.velocity;
2056 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
2058 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
2064 p = e.origin + t * e.velocity;
2066 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
2068 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2074 void func_vectormamamam_controller_think()
2076 self.nextthink = time + 0.1;
2078 if not (self.owner.active == ACTIVE_ACTIVE)
2080 self.owner.velocity = '0 0 0';
2084 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2085 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2088 void func_vectormamamam_findtarget()
2090 if(self.target != "")
2091 self.wp00 = find(world, targetname, self.target);
2093 if(self.target2 != "")
2094 self.wp01 = find(world, targetname, self.target2);
2096 if(self.target3 != "")
2097 self.wp02 = find(world, targetname, self.target3);
2099 if(self.target4 != "")
2100 self.wp03 = find(world, targetname, self.target4);
2102 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2103 objerror("No reference entity found, so there is nothing to move. Aborting.");
2105 self.destvec = self.origin - func_vectormamamam_origin(self, 0);
2108 controller = spawn();
2109 controller.classname = "func_vectormamamam_controller";
2110 controller.owner = self;
2111 controller.nextthink = time + 1;
2112 controller.think = func_vectormamamam_controller_think;
2115 void spawnfunc_func_vectormamamam()
2117 if (self.noise != "")
2119 precache_sound(self.noise);
2120 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
2123 if(!self.targetfactor)
2124 self.targetfactor = 1;
2126 if(!self.target2factor)
2127 self.target2factor = 1;
2129 if(!self.target3factor)
2130 self.target3factor = 1;
2132 if(!self.target4factor)
2133 self.target4factor = 1;
2135 if(vlen(self.targetnormal))
2136 self.targetnormal = normalize(self.targetnormal);
2138 if(vlen(self.target2normal))
2139 self.target2normal = normalize(self.target2normal);
2141 if(vlen(self.target3normal))
2142 self.target3normal = normalize(self.target3normal);
2144 if(vlen(self.target4normal))
2145 self.target4normal = normalize(self.target4normal);
2147 self.blocked = generic_plat_blocked;
2148 if(self.dmg && (self.message == ""))
2149 self.message = " was squished";
2150 if(self.dmg && (self.message == ""))
2151 self.message2 = "was squished by";
2152 if(self.dmg && (!self.dmgtime))
2153 self.dmgtime = 0.25;
2154 self.dmgtime2 = time;
2156 if(self.netname == "")
2157 self.netname = "1 0 0 0 1";
2159 if not(InitMovingBrushTrigger())
2162 // wait for targets to spawn
2163 self.nextthink = self.ltime + 999999999;
2164 self.think = SUB_NullThink; // for PushMove
2166 // Savage: Reduce bandwith, critical on e.g. nexdm02
2167 self.effects |= EF_LOWPRECISION;
2169 self.active = ACTIVE_ACTIVE;
2171 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2174 void conveyor_think()
2178 // set myself as current conveyor where possible
2179 for(e = world; (e = findentity(e, conveyor, self)); )
2184 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2185 if(!e.conveyor.state)
2188 vector emin = e.absmin;
2189 vector emax = e.absmax;
2190 if(self.solid == SOLID_BSP)
2195 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2196 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2200 for(e = world; (e = findentity(e, conveyor, self)); )
2202 if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
2203 continue; // done in SV_PlayerPhysics
2205 setorigin(e, e.origin + self.movedir * sys_frametime);
2206 move_out_of_solid(e);
2207 UpdateCSQCProjectile(e);
2209 // stupid conveyor code
2210 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2211 if(trace_fraction > 0)
2212 setorigin(e, trace_endpos);
2217 self.nextthink = time;
2222 self.state = !self.state;
2225 void conveyor_reset()
2227 self.state = (self.spawnflags & 1);
2230 void conveyor_init()
2234 self.movedir = self.movedir * self.speed;
2235 self.think = conveyor_think;
2236 self.nextthink = time;
2239 self.use = conveyor_use;
2240 self.reset = conveyor_reset;
2247 void spawnfunc_trigger_conveyor()
2254 void spawnfunc_func_conveyor()
2257 InitMovingBrushTrigger();
2258 self.movetype = MOVETYPE_NONE;