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 using bezier curves and turning is enabled, the train will turn toward the next point while waiting
302 if(!self.train_wait_turning)
303 if(self.spawnflags & 1 && self.bezier_turn && self.wait >= 0)
307 targ = find(world, targetname, self.target);
308 org = normalize(targ.origin);
309 SUB_CalcAngleMove(org, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
310 self.train_wait_turning = TRUE;
315 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
317 if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
319 self.train_wait_turning = FALSE;
324 self.think = train_next;
325 self.nextthink = self.ltime + self.wait;
334 targ = find(world, targetname, self.target);
335 self.target = targ.target;
336 if (self.spawnflags & 1)
338 cp = find(world, target, targ.targetname); // get the previous corner first
339 cp = find(world, targetname, cp.curve); // now get its second target (the control point)
340 if(cp.targetname == "")
341 cp_org = targ.origin - self.mins; // no control point found, assume a straight line to the destination
343 cp_org = cp.origin - self.mins;
346 objerror("train_next: no next target");
347 self.wait = targ.wait;
351 if(targ.platmovetype_start || targ.platmovetype_end)
353 // this path_corner contains a movetype overrider, apply it
354 self.platmovetype_start = targ.platmovetype_start;
355 self.platmovetype_end = targ.platmovetype_end;
359 // this path_corner doesn't contain a movetype overrider, use the train's defaults
360 self.platmovetype_start = self.platmovetype_start_default;
361 self.platmovetype_end = self.platmovetype_end_default;
366 if (self.spawnflags & 1)
367 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
369 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, targ.speed, train_wait);
373 if (self.spawnflags & 1)
374 SUB_CalcMove_Bezier(cp_org, targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
376 SUB_CalcMove(targ.origin - self.mins, TSPEED_LINEAR, self.speed, train_wait);
380 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
383 void func_train_find()
386 targ = find(world, targetname, self.target);
387 self.target = targ.target;
389 objerror("func_train_find: no next target");
390 setorigin(self, targ.origin - self.mins);
391 self.nextthink = self.ltime + 1;
392 self.think = train_next;
395 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
396 Ridable platform, targets spawnfunc_path_corner path to follow.
397 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
398 target : targetname of first spawnfunc_path_corner (starts here)
400 void spawnfunc_func_train()
402 if (self.noise != "")
403 precache_sound(self.noise);
406 objerror("func_train without a target");
409 if (self.spawnflags & 2)
410 self.bezier_turn = TRUE;
412 if not(InitMovingBrushTrigger())
414 self.effects |= EF_LOWPRECISION;
416 // wait for targets to spawn
417 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
419 self.blocked = generic_plat_blocked;
420 if(self.dmg & (!self.message))
421 self.message = " was squished";
422 if(self.dmg && (!self.message2))
423 self.message2 = "was squished by";
424 if(self.dmg && (!self.dmgtime))
426 self.dmgtime2 = time;
428 set_platmovetype(self, self.platmovetype);
429 self.platmovetype_start_default = self.platmovetype_start;
430 self.platmovetype_end_default = self.platmovetype_end;
432 // TODO make a reset function for this one
435 void func_rotating_setactive(float astate)
438 if (astate == ACTIVE_TOGGLE)
440 if(self.active == ACTIVE_ACTIVE)
441 self.active = ACTIVE_NOT;
443 self.active = ACTIVE_ACTIVE;
446 self.active = astate;
448 if(self.active == ACTIVE_NOT)
449 self.avelocity = '0 0 0';
451 self.avelocity = self.pos1;
454 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
455 Brush model that spins in place on one axis (default Z).
456 speed : speed to rotate (in degrees per second)
457 noise : path/name of looping .wav file to play.
458 dmg : Do this mutch dmg every .dmgtime intervall when blocked
462 void spawnfunc_func_rotating()
464 if (self.noise != "")
466 precache_sound(self.noise);
467 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
470 self.active = ACTIVE_ACTIVE;
471 self.setactive = func_rotating_setactive;
475 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
476 if (self.spawnflags & 4) // X (untested)
477 self.avelocity = '0 0 1' * self.speed;
478 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
479 else if (self.spawnflags & 8) // Y (untested)
480 self.avelocity = '1 0 0' * self.speed;
481 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
483 self.avelocity = '0 1 0' * self.speed;
485 self.pos1 = self.avelocity;
487 if(self.dmg & (!self.message))
488 self.message = " was squished";
489 if(self.dmg && (!self.message2))
490 self.message2 = "was squished by";
493 if(self.dmg && (!self.dmgtime))
496 self.dmgtime2 = time;
498 if not(InitMovingBrushTrigger())
500 // no EF_LOWPRECISION here, as rounding angles is bad
502 self.blocked = generic_plat_blocked;
504 // wait for targets to spawn
505 self.nextthink = self.ltime + 999999999;
506 self.think = SUB_Null;
508 // TODO make a reset function for this one
512 void func_bobbing_controller_think()
515 self.nextthink = time + 0.1;
517 if not (self.owner.active == ACTIVE_ACTIVE)
519 self.owner.velocity = '0 0 0';
523 // calculate sinewave using makevectors
524 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
525 v = self.owner.destvec + self.owner.movedir * v_forward_y;
526 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
527 // * 10 so it will arrive in 0.1 sec
528 self.owner.velocity = (v - self.owner.origin) * 10;
531 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
532 Brush model that moves back and forth on one axis (default Z).
533 speed : how long one cycle takes in seconds (default 4)
534 height : how far the cycle moves (default 32)
535 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
536 noise : path/name of looping .wav file to play.
537 dmg : Do this mutch dmg every .dmgtime intervall when blocked
540 void spawnfunc_func_bobbing()
543 if (self.noise != "")
545 precache_sound(self.noise);
546 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
552 // center of bobbing motion
553 self.destvec = self.origin;
554 // time scale to get degrees
555 self.cnt = 360 / self.speed;
557 self.active = ACTIVE_ACTIVE;
559 // damage when blocked
560 self.blocked = generic_plat_blocked;
561 if(self.dmg & (!self.message))
562 self.message = " was squished";
563 if(self.dmg && (!self.message2))
564 self.message2 = "was squished by";
565 if(self.dmg && (!self.dmgtime))
567 self.dmgtime2 = time;
570 if (self.spawnflags & 1) // X
571 self.movedir = '1 0 0' * self.height;
572 else if (self.spawnflags & 2) // Y
573 self.movedir = '0 1 0' * self.height;
575 self.movedir = '0 0 1' * self.height;
577 if not(InitMovingBrushTrigger())
580 // wait for targets to spawn
581 controller = spawn();
582 controller.classname = "func_bobbing_controller";
583 controller.owner = self;
584 controller.nextthink = time + 1;
585 controller.think = func_bobbing_controller_think;
586 self.nextthink = self.ltime + 999999999;
587 self.think = SUB_Null;
589 // Savage: Reduce bandwith, critical on e.g. nexdm02
590 self.effects |= EF_LOWPRECISION;
592 // TODO make a reset function for this one
596 void func_pendulum_controller_think()
599 self.nextthink = time + 0.1;
601 if not (self.owner.active == ACTIVE_ACTIVE)
603 self.owner.avelocity_x = 0;
607 // calculate sinewave using makevectors
608 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
609 v = self.owner.speed * v_forward_y + self.cnt;
610 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
612 // * 10 so it will arrive in 0.1 sec
613 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
617 void spawnfunc_func_pendulum()
620 if (self.noise != "")
622 precache_sound(self.noise);
623 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
626 self.active = ACTIVE_ACTIVE;
628 // keys: angle, speed, phase, noise, freq
632 // not initializing self.dmg to 2, to allow damageless pendulum
634 if(self.dmg & (!self.message))
635 self.message = " was squished";
636 if(self.dmg && (!self.message2))
637 self.message2 = "was squished by";
638 if(self.dmg && (!self.dmgtime))
640 self.dmgtime2 = time;
642 self.blocked = generic_plat_blocked;
644 self.avelocity_z = 0.0000001;
645 if not(InitMovingBrushTrigger())
650 // find pendulum length (same formula as Q3A)
651 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
654 // copy initial angle
655 self.cnt = self.angles_z;
657 // wait for targets to spawn
658 controller = spawn();
659 controller.classname = "func_pendulum_controller";
660 controller.owner = self;
661 controller.nextthink = time + 1;
662 controller.think = func_pendulum_controller_think;
663 self.nextthink = self.ltime + 999999999;
664 self.think = SUB_Null;
666 //self.effects |= EF_LOWPRECISION;
668 // TODO make a reset function for this one
671 // button and multiple button
674 void() button_return;
678 self.state = STATE_TOP;
679 self.nextthink = self.ltime + self.wait;
680 self.think = button_return;
681 activator = self.enemy;
683 self.frame = 1; // use alternate textures
688 self.state = STATE_BOTTOM;
693 self.state = STATE_DOWN;
694 SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
695 self.frame = 0; // use normal textures
697 self.takedamage = DAMAGE_YES; // can be shot again
701 void button_blocked()
703 // do nothing, just don't come all the way back out
709 self.health = self.max_health;
710 self.takedamage = DAMAGE_NO; // will be reset upon return
712 if (self.state == STATE_UP || self.state == STATE_TOP)
715 if (self.noise != "")
716 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
718 self.state = STATE_UP;
719 SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
724 self.health = self.max_health;
725 setorigin(self, self.pos1);
726 self.frame = 0; // use normal textures
727 self.state = STATE_BOTTOM;
729 self.takedamage = DAMAGE_YES; // can be shot again
734 // if (activator.classname != "player")
736 // dprint(activator.classname);
737 // dprint(" triggered a button\n");
740 if not (self.active == ACTIVE_ACTIVE)
743 self.enemy = activator;
749 // if (activator.classname != "player")
751 // dprint(activator.classname);
752 // dprint(" touched a button\n");
756 if not(other.iscreature)
758 if(other.velocity * self.movedir < 0)
762 self.enemy = other.owner;
766 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
768 if(self.spawnflags & DOOR_NOSPLASH)
769 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
771 self.health = self.health - damage;
772 if (self.health <= 0)
774 // if (activator.classname != "player")
776 // dprint(activator.classname);
777 // dprint(" killed a button\n");
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 float DOOR_START_OPEN = 1;
843 float DOOR_DONT_LINK = 4;
844 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, ATTN_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, ATTN_NORM);
945 self.state = STATE_BOTTOM;
950 if (self.noise2 != "")
951 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_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, ATTN_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 (other.classname != "player")
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(other.classname != "player")
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 (other.flags & FL_CLIENT)
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, ATTN_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, ATTN_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, ATTN_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, ATTN_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 float EntitiesTouching(entity e1, entity e2)
1319 if (e1.absmin_x > e2.absmax_x)
1321 if (e1.absmin_y > e2.absmax_y)
1323 if (e1.absmin_z > e2.absmax_z)
1325 if (e1.absmax_x < e2.absmin_x)
1327 if (e1.absmax_y < e2.absmin_y)
1329 if (e1.absmax_z < e2.absmin_z)
1345 vector cmins, cmaxs;
1348 return; // already linked by another door
1349 if (self.spawnflags & 4)
1351 self.owner = self.enemy = self;
1359 self.trigger_field = spawn_field(self.absmin, self.absmax);
1361 return; // don't want to link this door
1364 cmins = self.absmin;
1365 cmaxs = self.absmax;
1372 self.owner = starte; // master door
1375 starte.health = self.health;
1377 starte.targetname = self.targetname;
1378 if (self.message != "")
1379 starte.message = self.message;
1381 t = find(t, classname, self.classname);
1384 self.enemy = starte; // make the chain a loop
1386 // shootable, or triggered doors just needed the owner/enemy links,
1387 // they don't spawn a field
1398 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1403 if (EntitiesTouching(self,t))
1406 objerror ("cross connected doors");
1411 if (t.absmin_x < cmins_x)
1412 cmins_x = t.absmin_x;
1413 if (t.absmin_y < cmins_y)
1414 cmins_y = t.absmin_y;
1415 if (t.absmin_z < cmins_z)
1416 cmins_z = t.absmin_z;
1417 if (t.absmax_x > cmaxs_x)
1418 cmaxs_x = t.absmax_x;
1419 if (t.absmax_y > cmaxs_y)
1420 cmaxs_y = t.absmax_y;
1421 if (t.absmax_z > cmaxs_z)
1422 cmaxs_z = t.absmax_z;
1429 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1430 if two doors touch, they are assumed to be connected and operate as a unit.
1432 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1434 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).
1436 GOLD_KEY causes the door to open only if the activator holds a gold key.
1438 SILVER_KEY causes the door to open only if the activator holds a silver key.
1440 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1441 "angle" determines the opening direction
1442 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1443 "health" if set, door must be shot open
1444 "speed" movement speed (100 default)
1445 "wait" wait before returning (3 default, -1 = never return)
1446 "lip" lip remaining at end of move (8 default)
1447 "dmg" damage to inflict when blocked (2 default)
1454 FIXME: only one sound set available at the time being
1458 void door_init_startopen()
1460 setorigin (self, self.pos2);
1461 self.pos2 = self.pos1;
1462 self.pos1 = self.origin;
1467 setorigin(self, self.pos1);
1468 self.velocity = '0 0 0';
1469 self.state = STATE_BOTTOM;
1470 self.think = SUB_Null;
1473 // spawnflags require key (for now only func_door)
1474 #define SPAWNFLAGS_GOLD_KEY 8
1475 #define SPAWNFLAGS_SILVER_KEY 16
1476 void spawnfunc_func_door()
1478 // Quake 1 keys compatibility
1479 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1480 self.itemkeys |= ITEM_KEY_BIT(0);
1481 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1482 self.itemkeys |= ITEM_KEY_BIT(1);
1484 //if (!self.deathtype) // map makers can override this
1485 // self.deathtype = " got in the way";
1488 self.max_health = self.health;
1489 if not(InitMovingBrushTrigger())
1491 self.effects |= EF_LOWPRECISION;
1492 self.classname = "door";
1494 self.blocked = door_blocked;
1495 self.use = door_use;
1497 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1498 // if(self.spawnflags & 8)
1499 // self.dmg = 10000;
1501 if(self.dmg && (!self.message))
1502 self.message = "was squished";
1503 if(self.dmg && (!self.message2))
1504 self.message2 = "was squished by";
1506 if (self.sounds > 0)
1508 precache_sound ("plats/medplat1.wav");
1509 precache_sound ("plats/medplat2.wav");
1510 self.noise2 = "plats/medplat1.wav";
1511 self.noise1 = "plats/medplat2.wav";
1521 self.pos1 = self.origin;
1522 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1524 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1525 // but spawn in the open position
1526 if (self.spawnflags & DOOR_START_OPEN)
1527 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1529 self.state = STATE_BOTTOM;
1533 self.takedamage = DAMAGE_YES;
1534 self.event_damage = door_damage;
1540 self.touch = door_touch;
1542 // LinkDoors can't be done until all of the doors have been spawned, so
1543 // the sizes can be detected properly.
1544 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1546 self.reset = door_reset;
1549 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1550 if two doors touch, they are assumed to be connected and operate as a unit.
1552 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1554 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1555 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1556 must have set trigger_reverse to 1.
1557 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1559 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).
1561 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1562 "angle" determines the destination angle for opening. negative values reverse the direction.
1563 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1564 "health" if set, door must be shot open
1565 "speed" movement speed (100 default)
1566 "wait" wait before returning (3 default, -1 = never return)
1567 "dmg" damage to inflict when blocked (2 default)
1574 FIXME: only one sound set available at the time being
1577 void door_rotating_reset()
1579 self.angles = self.pos1;
1580 self.avelocity = '0 0 0';
1581 self.state = STATE_BOTTOM;
1582 self.think = SUB_Null;
1585 void door_rotating_init_startopen()
1587 self.angles = self.movedir;
1588 self.pos2 = '0 0 0';
1589 self.pos1 = self.movedir;
1593 void spawnfunc_func_door_rotating()
1596 //if (!self.deathtype) // map makers can override this
1597 // self.deathtype = " got in the way";
1599 // I abuse "movedir" for denoting the axis for now
1600 if (self.spawnflags & 64) // X (untested)
1601 self.movedir = '0 0 1';
1602 else if (self.spawnflags & 128) // Y (untested)
1603 self.movedir = '1 0 0';
1605 self.movedir = '0 1 0';
1607 if (self.angles_y==0) self.angles_y = 90;
1609 self.movedir = self.movedir * self.angles_y;
1610 self.angles = '0 0 0';
1612 self.max_health = self.health;
1613 self.avelocity = self.movedir;
1614 if not(InitMovingBrushTrigger())
1616 self.velocity = '0 0 0';
1617 //self.effects |= EF_LOWPRECISION;
1618 self.classname = "door_rotating";
1620 self.blocked = door_blocked;
1621 self.use = door_use;
1623 if(self.spawnflags & 8)
1626 if(self.dmg && (!self.message))
1627 self.message = "was squished";
1628 if(self.dmg && (!self.message2))
1629 self.message2 = "was squished by";
1631 if (self.sounds > 0)
1633 precache_sound ("plats/medplat1.wav");
1634 precache_sound ("plats/medplat2.wav");
1635 self.noise2 = "plats/medplat1.wav";
1636 self.noise1 = "plats/medplat2.wav";
1643 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1645 self.pos1 = '0 0 0';
1646 self.pos2 = self.movedir;
1648 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1649 // but spawn in the open position
1650 if (self.spawnflags & DOOR_START_OPEN)
1651 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1653 self.state = STATE_BOTTOM;
1657 self.takedamage = DAMAGE_YES;
1658 self.event_damage = door_damage;
1664 self.touch = door_touch;
1666 // LinkDoors can't be done until all of the doors have been spawned, so
1667 // the sizes can be detected properly.
1668 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1670 self.reset = door_rotating_reset;
1674 =============================================================================
1678 =============================================================================
1681 void() fd_secret_move1;
1682 void() fd_secret_move2;
1683 void() fd_secret_move3;
1684 void() fd_secret_move4;
1685 void() fd_secret_move5;
1686 void() fd_secret_move6;
1687 void() fd_secret_done;
1689 float SECRET_OPEN_ONCE = 1; // stays open
1690 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1691 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1692 float SECRET_NO_SHOOT = 8; // only opened by trigger
1693 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1696 void fd_secret_use()
1699 string message_save;
1701 self.health = 10000;
1702 self.bot_attack = TRUE;
1704 // exit if still moving around...
1705 if (self.origin != self.oldorigin)
1708 message_save = self.message;
1709 self.message = ""; // no more message
1710 SUB_UseTargets(); // fire all targets / killtargets
1711 self.message = message_save;
1713 self.velocity = '0 0 0';
1715 // Make a sound, wait a little...
1717 if (self.noise1 != "")
1718 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1719 self.nextthink = self.ltime + 0.1;
1721 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1722 makevectors(self.mangle);
1726 if (self.spawnflags & SECRET_1ST_DOWN)
1727 self.t_width = fabs(v_up * self.size);
1729 self.t_width = fabs(v_right * self.size);
1733 self.t_length = fabs(v_forward * self.size);
1735 if (self.spawnflags & SECRET_1ST_DOWN)
1736 self.dest1 = self.origin - v_up * self.t_width;
1738 self.dest1 = self.origin + v_right * (self.t_width * temp);
1740 self.dest2 = self.dest1 + v_forward * self.t_length;
1741 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
1742 if (self.noise2 != "")
1743 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1746 // Wait after first movement...
1747 void fd_secret_move1()
1749 self.nextthink = self.ltime + 1.0;
1750 self.think = fd_secret_move2;
1751 if (self.noise3 != "")
1752 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1755 // Start moving sideways w/sound...
1756 void fd_secret_move2()
1758 if (self.noise2 != "")
1759 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1760 SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
1763 // Wait here until time to go back...
1764 void fd_secret_move3()
1766 if (self.noise3 != "")
1767 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1768 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1770 self.nextthink = self.ltime + self.wait;
1771 self.think = fd_secret_move4;
1776 void fd_secret_move4()
1778 if (self.noise2 != "")
1779 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1780 SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
1784 void fd_secret_move5()
1786 self.nextthink = self.ltime + 1.0;
1787 self.think = fd_secret_move6;
1788 if (self.noise3 != "")
1789 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1792 void fd_secret_move6()
1794 if (self.noise2 != "")
1795 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1796 SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
1799 void fd_secret_done()
1801 if (self.spawnflags&SECRET_YES_SHOOT)
1803 self.health = 10000;
1804 self.takedamage = DAMAGE_YES;
1805 //self.th_pain = fd_secret_use;
1807 if (self.noise3 != "")
1808 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1811 void secret_blocked()
1813 if (time < self.attack_finished_single)
1815 self.attack_finished_single = time + 0.5;
1816 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1828 if not(other.iscreature)
1830 if (self.attack_finished_single > time)
1833 self.attack_finished_single = time + 2;
1837 if (other.flags & FL_CLIENT)
1838 centerprint (other, self.message);
1839 play2(other, "misc/talk.wav");
1845 if (self.spawnflags&SECRET_YES_SHOOT)
1847 self.health = 10000;
1848 self.takedamage = DAMAGE_YES;
1850 setorigin(self, self.oldorigin);
1851 self.think = SUB_Null;
1854 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1855 Basic secret door. Slides back, then to the side. Angle determines direction.
1856 wait = # of seconds before coming back
1857 1st_left = 1st move is left of arrow
1858 1st_down = 1st move is down from arrow
1859 always_shoot = even if targeted, keep shootable
1860 t_width = override WIDTH to move back (or height if going down)
1861 t_length = override LENGTH to move sideways
1862 "dmg" damage to inflict when blocked (2 default)
1864 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1871 void spawnfunc_func_door_secret()
1873 /*if (!self.deathtype) // map makers can override this
1874 self.deathtype = " got in the way";*/
1880 self.mangle = self.angles;
1881 self.angles = '0 0 0';
1882 self.classname = "door";
1883 if not(InitMovingBrushTrigger())
1885 self.effects |= EF_LOWPRECISION;
1887 self.touch = secret_touch;
1888 self.blocked = secret_blocked;
1890 self.use = fd_secret_use;
1895 self.spawnflags |= SECRET_YES_SHOOT;
1897 if(self.spawnflags&SECRET_YES_SHOOT)
1899 self.health = 10000;
1900 self.takedamage = DAMAGE_YES;
1901 self.event_damage = fd_secret_use;
1903 self.oldorigin = self.origin;
1905 self.wait = 5; // 5 seconds before closing
1907 self.reset = secret_reset;
1911 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1912 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1913 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
1914 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1915 height: amplitude modifier (default 32)
1916 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1917 noise: path/name of looping .wav file to play.
1918 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1922 void func_fourier_controller_think()
1927 self.nextthink = time + 0.1;
1928 if not (self.owner.active == ACTIVE_ACTIVE)
1930 self.owner.velocity = '0 0 0';
1935 n = floor((tokenize_console(self.owner.netname)) / 5);
1936 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1938 v = self.owner.destvec;
1940 for(i = 0; i < n; ++i)
1942 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1943 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;
1946 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1947 // * 10 so it will arrive in 0.1 sec
1948 self.owner.velocity = (v - self.owner.origin) * 10;
1951 void spawnfunc_func_fourier()
1954 if (self.noise != "")
1956 precache_sound(self.noise);
1957 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1964 self.destvec = self.origin;
1965 self.cnt = 360 / self.speed;
1967 self.blocked = generic_plat_blocked;
1968 if(self.dmg & (!self.message))
1969 self.message = " was squished";
1970 if(self.dmg && (!self.message2))
1971 self.message2 = "was squished by";
1972 if(self.dmg && (!self.dmgtime))
1973 self.dmgtime = 0.25;
1974 self.dmgtime2 = time;
1976 if(self.netname == "")
1977 self.netname = "1 0 0 0 1";
1979 if not(InitMovingBrushTrigger())
1982 self.active = ACTIVE_ACTIVE;
1984 // wait for targets to spawn
1985 controller = spawn();
1986 controller.classname = "func_fourier_controller";
1987 controller.owner = self;
1988 controller.nextthink = time + 1;
1989 controller.think = func_fourier_controller_think;
1990 self.nextthink = self.ltime + 999999999;
1991 self.think = SUB_Null;
1993 // Savage: Reduce bandwith, critical on e.g. nexdm02
1994 self.effects |= EF_LOWPRECISION;
1996 // TODO make a reset function for this one
1999 // reusing some fields havocbots declared
2000 .entity wp00, wp01, wp02, wp03;
2002 .float targetfactor, target2factor, target3factor, target4factor;
2003 .vector targetnormal, target2normal, target3normal, target4normal;
2005 vector func_vectormamamam_origin(entity o, float t)
2017 p = e.origin + t * e.velocity;
2019 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
2021 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
2027 p = e.origin + t * e.velocity;
2029 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
2031 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
2037 p = e.origin + t * e.velocity;
2039 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
2041 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
2047 p = e.origin + t * e.velocity;
2049 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
2051 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
2057 void func_vectormamamam_controller_think()
2059 self.nextthink = time + 0.1;
2061 if not (self.owner.active == ACTIVE_ACTIVE)
2063 self.owner.velocity = '0 0 0';
2067 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
2068 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
2071 void func_vectormamamam_findtarget()
2073 if(self.target != "")
2074 self.wp00 = find(world, targetname, self.target);
2076 if(self.target2 != "")
2077 self.wp01 = find(world, targetname, self.target2);
2079 if(self.target3 != "")
2080 self.wp02 = find(world, targetname, self.target3);
2082 if(self.target4 != "")
2083 self.wp03 = find(world, targetname, self.target4);
2085 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2086 objerror("No reference entity found, so there is nothing to move. Aborting.");
2088 self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
2091 controller = spawn();
2092 controller.classname = "func_vectormamamam_controller";
2093 controller.owner = self;
2094 controller.nextthink = time + 1;
2095 controller.think = func_vectormamamam_controller_think;
2098 void spawnfunc_func_vectormamamam()
2100 if (self.noise != "")
2102 precache_sound(self.noise);
2103 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2106 if(!self.targetfactor)
2107 self.targetfactor = 1;
2109 if(!self.target2factor)
2110 self.target2factor = 1;
2112 if(!self.target3factor)
2113 self.target3factor = 1;
2115 if(!self.target4factor)
2116 self.target4factor = 1;
2118 if(vlen(self.targetnormal))
2119 self.targetnormal = normalize(self.targetnormal);
2121 if(vlen(self.target2normal))
2122 self.target2normal = normalize(self.target2normal);
2124 if(vlen(self.target3normal))
2125 self.target3normal = normalize(self.target3normal);
2127 if(vlen(self.target4normal))
2128 self.target4normal = normalize(self.target4normal);
2130 self.blocked = generic_plat_blocked;
2131 if(self.dmg & (!self.message))
2132 self.message = " was squished";
2133 if(self.dmg && (!self.message2))
2134 self.message2 = "was squished by";
2135 if(self.dmg && (!self.dmgtime))
2136 self.dmgtime = 0.25;
2137 self.dmgtime2 = time;
2139 if(self.netname == "")
2140 self.netname = "1 0 0 0 1";
2142 if not(InitMovingBrushTrigger())
2145 // wait for targets to spawn
2146 self.nextthink = self.ltime + 999999999;
2147 self.think = SUB_Null;
2149 // Savage: Reduce bandwith, critical on e.g. nexdm02
2150 self.effects |= EF_LOWPRECISION;
2152 self.active = ACTIVE_ACTIVE;
2154 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2157 void conveyor_think()
2161 // set myself as current conveyor where possible
2162 for(e = world; (e = findentity(e, conveyor, self)); )
2167 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2168 if(!e.conveyor.state)
2171 vector emin = e.absmin;
2172 vector emax = e.absmax;
2173 if(self.solid == SOLID_BSP)
2178 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2179 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2183 for(e = world; (e = findentity(e, conveyor, self)); )
2185 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2186 continue; // done in SV_PlayerPhysics
2188 setorigin(e, e.origin + self.movedir * sys_frametime);
2189 move_out_of_solid(e);
2190 UpdateCSQCProjectile(e);
2192 // stupid conveyor code
2193 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2194 if(trace_fraction > 0)
2195 setorigin(e, trace_endpos);
2200 self.nextthink = time;
2205 self.state = !self.state;
2208 void conveyor_reset()
2210 self.state = (self.spawnflags & 1);
2213 void conveyor_init()
2217 self.movedir = self.movedir * self.speed;
2218 self.think = conveyor_think;
2219 self.nextthink = time;
2222 self.use = conveyor_use;
2223 self.reset = conveyor_reset;
2230 void spawnfunc_trigger_conveyor()
2237 void spawnfunc_func_conveyor()
2240 InitMovingBrushTrigger();
2241 self.movetype = MOVETYPE_NONE;