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, self.speed, plat_hit_bottom);
96 sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
98 SUB_CalcMove (self.pos1, 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!
161 self.use = func_null;
163 objerror ("plat_use: not in up state");
167 .string sound1, sound2;
173 setorigin (self, self.pos1);
179 setorigin (self, self.pos2);
181 self.use = plat_trigger_use;
185 void spawnfunc_path_corner() { }
186 void spawnfunc_func_plat()
188 if (self.sounds == 0)
191 if(self.spawnflags & 4)
194 if(self.dmg && (self.message == ""))
195 self.message = "was squished";
196 if(self.dmg && (self.message2 == ""))
197 self.message2 = "was squished by";
199 if (self.sounds == 1)
201 precache_sound ("plats/plat1.wav");
202 precache_sound ("plats/plat2.wav");
203 self.noise = "plats/plat1.wav";
204 self.noise1 = "plats/plat2.wav";
207 if (self.sounds == 2)
209 precache_sound ("plats/medplat1.wav");
210 precache_sound ("plats/medplat2.wav");
211 self.noise = "plats/medplat1.wav";
212 self.noise1 = "plats/medplat2.wav";
217 precache_sound (self.sound1);
218 self.noise = self.sound1;
222 precache_sound (self.sound2);
223 self.noise1 = self.sound2;
226 self.mangle = self.angles;
227 self.angles = '0 0 0';
229 self.classname = "plat";
230 if not(InitMovingBrushTrigger())
232 self.effects |= EF_LOWPRECISION;
233 setsize (self, self.mins , self.maxs);
235 self.blocked = plat_crush;
242 self.height = self.size_z - self.lip;
244 self.pos1 = self.origin;
245 self.pos2 = self.origin;
246 self.pos2_z = self.origin_z - self.height;
248 self.reset = plat_reset;
251 plat_spawn_inside_trigger (); // the "start moving" trigger
259 stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
267 self.think = train_next;
268 self.nextthink = self.ltime + self.wait;
282 targ = find(world, targetname, self.target);
284 self.target = targ.target;
285 if (self.target == "")
286 objerror("train_next: no next target");
287 self.wait = targ.wait;
292 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
294 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
297 sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
300 void func_train_find()
303 targ = find(world, targetname, self.target);
304 self.target = targ.target;
305 if (self.target == "")
306 objerror("func_train_find: no next target");
307 setorigin(self, targ.origin - self.mins);
308 self.nextthink = self.ltime + 1;
309 self.think = train_next;
312 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
313 Ridable platform, targets spawnfunc_path_corner path to follow.
314 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
315 target : targetname of first spawnfunc_path_corner (starts here)
317 void spawnfunc_func_train()
319 if (self.noise != "")
320 precache_sound(self.noise);
322 if (self.target == "")
323 objerror("func_train without a target");
327 if not(InitMovingBrushTrigger())
329 self.effects |= EF_LOWPRECISION;
331 // wait for targets to spawn
332 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
334 self.blocked = generic_plat_blocked;
335 if(self.dmg && (self.message == ""))
336 self.message = " was squished";
337 if(self.dmg && (self.message2 == ""))
338 self.message2 = "was squished by";
339 if(self.dmg && (!self.dmgtime))
341 self.dmgtime2 = time;
343 // TODO make a reset function for this one
346 void func_rotating_setactive(float astate)
349 if (astate == ACTIVE_TOGGLE)
351 if(self.active == ACTIVE_ACTIVE)
352 self.active = ACTIVE_NOT;
354 self.active = ACTIVE_ACTIVE;
357 self.active = astate;
359 if(self.active == ACTIVE_NOT)
360 self.avelocity = '0 0 0';
362 self.avelocity = self.pos1;
365 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
366 Brush model that spins in place on one axis (default Z).
367 speed : speed to rotate (in degrees per second)
368 noise : path/name of looping .wav file to play.
369 dmg : Do this mutch dmg every .dmgtime intervall when blocked
373 void spawnfunc_func_rotating()
375 if (self.noise != "")
377 precache_sound(self.noise);
378 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
381 self.active = ACTIVE_ACTIVE;
382 self.setactive = func_rotating_setactive;
386 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
387 if (self.spawnflags & 4) // X (untested)
388 self.avelocity = '0 0 1' * self.speed;
389 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
390 else if (self.spawnflags & 8) // Y (untested)
391 self.avelocity = '1 0 0' * self.speed;
392 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
394 self.avelocity = '0 1 0' * self.speed;
396 self.pos1 = self.avelocity;
398 if(self.dmg && (self.message == ""))
399 self.message = " was squished";
400 if(self.dmg && (self.message2 == ""))
401 self.message2 = "was squished by";
404 if(self.dmg && (!self.dmgtime))
407 self.dmgtime2 = time;
409 if not(InitMovingBrushTrigger())
411 // no EF_LOWPRECISION here, as rounding angles is bad
413 self.blocked = generic_plat_blocked;
415 // wait for targets to spawn
416 self.nextthink = self.ltime + 999999999;
417 self.think = SUB_NullThink; // for PushMove
419 // TODO make a reset function for this one
423 void func_bobbing_controller_think()
426 self.nextthink = time + 0.1;
428 if not (self.owner.active == ACTIVE_ACTIVE)
430 self.owner.velocity = '0 0 0';
434 // calculate sinewave using makevectors
435 makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
436 v = self.owner.destvec + self.owner.movedir * v_forward_y;
437 if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
438 // * 10 so it will arrive in 0.1 sec
439 self.owner.velocity = (v - self.owner.origin) * 10;
442 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
443 Brush model that moves back and forth on one axis (default Z).
444 speed : how long one cycle takes in seconds (default 4)
445 height : how far the cycle moves (default 32)
446 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
447 noise : path/name of looping .wav file to play.
448 dmg : Do this mutch dmg every .dmgtime intervall when blocked
451 void spawnfunc_func_bobbing()
454 if (self.noise != "")
456 precache_sound(self.noise);
457 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
463 // center of bobbing motion
464 self.destvec = self.origin;
465 // time scale to get degrees
466 self.cnt = 360 / self.speed;
468 self.active = ACTIVE_ACTIVE;
470 // damage when blocked
471 self.blocked = generic_plat_blocked;
472 if(self.dmg && (self.message == ""))
473 self.message = " was squished";
474 if(self.dmg && (self.message2 == ""))
475 self.message2 = "was squished by";
476 if(self.dmg && (!self.dmgtime))
478 self.dmgtime2 = time;
481 if (self.spawnflags & 1) // X
482 self.movedir = '1 0 0' * self.height;
483 else if (self.spawnflags & 2) // Y
484 self.movedir = '0 1 0' * self.height;
486 self.movedir = '0 0 1' * self.height;
488 if not(InitMovingBrushTrigger())
491 // wait for targets to spawn
492 controller = spawn();
493 controller.classname = "func_bobbing_controller";
494 controller.owner = self;
495 controller.nextthink = time + 1;
496 controller.think = func_bobbing_controller_think;
497 self.nextthink = self.ltime + 999999999;
498 self.think = SUB_NullThink; // for PushMove
500 // Savage: Reduce bandwith, critical on e.g. nexdm02
501 self.effects |= EF_LOWPRECISION;
503 // TODO make a reset function for this one
507 void func_pendulum_controller_think()
510 self.nextthink = time + 0.1;
512 if not (self.owner.active == ACTIVE_ACTIVE)
514 self.owner.avelocity_x = 0;
518 // calculate sinewave using makevectors
519 makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
520 v = self.owner.speed * v_forward_y + self.cnt;
521 if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
523 // * 10 so it will arrive in 0.1 sec
524 self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
528 void spawnfunc_func_pendulum()
531 if (self.noise != "")
533 precache_sound(self.noise);
534 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
537 self.active = ACTIVE_ACTIVE;
539 // keys: angle, speed, phase, noise, freq
543 // not initializing self.dmg to 2, to allow damageless pendulum
545 if(self.dmg && (self.message == ""))
546 self.message = " was squished";
547 if(self.dmg && (self.message2 == ""))
548 self.message2 = "was squished by";
549 if(self.dmg && (!self.dmgtime))
551 self.dmgtime2 = time;
553 self.blocked = generic_plat_blocked;
555 self.avelocity_z = 0.0000001;
556 if not(InitMovingBrushTrigger())
561 // find pendulum length (same formula as Q3A)
562 self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
565 // copy initial angle
566 self.cnt = self.angles_z;
568 // wait for targets to spawn
569 controller = spawn();
570 controller.classname = "func_pendulum_controller";
571 controller.owner = self;
572 controller.nextthink = time + 1;
573 controller.think = func_pendulum_controller_think;
574 self.nextthink = self.ltime + 999999999;
575 self.think = SUB_NullThink; // for PushMove
577 //self.effects |= EF_LOWPRECISION;
579 // TODO make a reset function for this one
582 // button and multiple button
585 void() button_return;
589 self.state = STATE_TOP;
590 self.nextthink = self.ltime + self.wait;
591 self.think = button_return;
592 activator = self.enemy;
594 self.frame = 1; // use alternate textures
599 self.state = STATE_BOTTOM;
604 self.state = STATE_DOWN;
605 SUB_CalcMove (self.pos1, self.speed, button_done);
606 self.frame = 0; // use normal textures
608 self.takedamage = DAMAGE_YES; // can be shot again
612 void button_blocked()
614 // do nothing, just don't come all the way back out
620 self.health = self.max_health;
621 self.takedamage = DAMAGE_NO; // will be reset upon return
623 if (self.state == STATE_UP || self.state == STATE_TOP)
626 if (self.noise != "")
627 sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
629 self.state = STATE_UP;
630 SUB_CalcMove (self.pos2, self.speed, button_wait);
635 self.health = self.max_health;
636 setorigin(self, self.pos1);
637 self.frame = 0; // use normal textures
638 self.state = STATE_BOTTOM;
640 self.takedamage = DAMAGE_YES; // can be shot again
645 // if (activator.classname != "player")
647 // dprint(activator.classname);
648 // dprint(" triggered a button\n");
651 if not (self.active == ACTIVE_ACTIVE)
654 self.enemy = activator;
660 // if (activator.classname != "player")
662 // dprint(activator.classname);
663 // dprint(" touched a button\n");
667 if not(other.iscreature)
669 if(other.velocity * self.movedir < 0)
673 self.enemy = other.owner;
677 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
679 if(self.spawnflags & DOOR_NOSPLASH)
680 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
682 self.health = self.health - damage;
683 if (self.health <= 0)
685 // if (activator.classname != "player")
687 // dprint(activator.classname);
688 // dprint(" killed a button\n");
690 self.enemy = damage_attacker;
696 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
697 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.
699 "angle" determines the opening direction
700 "target" all entities with a matching targetname will be used
701 "speed" override the default 40 speed
702 "wait" override the default 1 second wait (-1 = never return)
703 "lip" override the default 4 pixel lip remaining at end of move
704 "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
711 void spawnfunc_func_button()
715 if not(InitMovingBrushTrigger())
717 self.effects |= EF_LOWPRECISION;
719 self.blocked = button_blocked;
720 self.use = button_use;
722 // if (self.health == 0) // all buttons are now shootable
726 self.max_health = self.health;
727 self.event_damage = button_damage;
728 self.takedamage = DAMAGE_YES;
731 self.touch = button_touch;
741 precache_sound(self.noise);
743 self.active = ACTIVE_ACTIVE;
745 self.pos1 = self.origin;
746 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
747 self.flags |= FL_NOTARGET;
753 float DOOR_START_OPEN = 1;
754 float DOOR_DONT_LINK = 4;
755 float DOOR_TOGGLE = 32;
759 Doors are similar to buttons, but can spawn a fat trigger field around them
760 to open without a touch, and they link together to form simultanious
763 Door.owner is the master door. If there is only one door, it points to itself.
764 If multiple doors, all will point to a single one.
766 Door.enemy chains from the master door through all doors linked in the chain.
771 =============================================================================
775 =============================================================================
780 void() door_rotating_go_down;
781 void() door_rotating_go_up;
786 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
787 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
790 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
791 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
793 //Dont chamge direction for dead or dying stuff
794 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
797 if (self.state == STATE_DOWN)
798 if (self.classname == "door")
803 door_rotating_go_up ();
806 if (self.classname == "door")
811 door_rotating_go_down ();
815 //gib dying stuff just to make sure
816 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
817 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
821 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
822 // if a door has a negative wait, it would never come back if blocked,
823 // so let it just squash the object to death real fast
824 /* if (self.wait >= 0)
826 if (self.state == STATE_DOWN)
837 if (self.noise1 != "")
838 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
839 self.state = STATE_TOP;
840 if (self.spawnflags & DOOR_TOGGLE)
841 return; // don't come down automatically
842 if (self.classname == "door")
844 self.think = door_go_down;
847 self.think = door_rotating_go_down;
849 self.nextthink = self.ltime + self.wait;
852 void door_hit_bottom()
854 if (self.noise1 != "")
855 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
856 self.state = STATE_BOTTOM;
861 if (self.noise2 != "")
862 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
865 self.takedamage = DAMAGE_YES;
866 self.health = self.max_health;
869 self.state = STATE_DOWN;
870 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
875 if (self.state == STATE_UP)
876 return; // already going up
878 if (self.state == STATE_TOP)
879 { // reset top wait time
880 self.nextthink = self.ltime + self.wait;
884 if (self.noise2 != "")
885 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
886 self.state = STATE_UP;
887 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
890 oldmessage = self.message;
893 self.message = oldmessage;
899 =============================================================================
903 =============================================================================
906 float door_check_keys(void) {
916 if not(door.itemkeys)
919 // this door require a key
920 // only a player can have a key
921 if (other.classname != "player")
924 if (item_keys_usekey(door, other)) {
925 // some keys were used
926 if (other.key_door_messagetime <= time) {
927 play2(other, "misc/talk.wav");
928 centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
929 other.key_door_messagetime = time + 2;
933 if (other.key_door_messagetime <= time) {
934 play2(other, "misc/talk.wav");
935 centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
936 other.key_door_messagetime = time + 2;
941 // door is now unlocked
942 play2(other, "misc/talk.wav");
943 centerprint(other, "Door unlocked!");
955 if (self.owner != self)
956 objerror ("door_fire: self.owner != self");
960 if (self.spawnflags & DOOR_TOGGLE)
962 if (self.state == STATE_UP || self.state == STATE_TOP)
967 if (self.classname == "door")
973 door_rotating_go_down ();
976 } while ( (self != starte) && (self != world) );
982 // trigger all paired doors
986 if (self.classname == "door")
991 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
992 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
994 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
995 self.pos2 = '0 0 0' - self.pos2;
997 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
998 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
999 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
1001 door_rotating_go_up ();
1005 } while ( (self != starte) && (self != world) );
1014 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
1026 void door_trigger_touch()
1028 if (other.health < 1)
1029 if not(other.iscreature && other.deadflag == DEAD_NO)
1032 if (time < self.attack_finished_single)
1035 // check if door is locked
1036 if (!door_check_keys())
1039 self.attack_finished_single = time + 1;
1048 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1051 if(self.spawnflags & DOOR_NOSPLASH)
1052 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
1054 self.health = self.health - damage;
1056 if (self.itemkeys) {
1057 // don't allow opening doors through damage if keys are required
1061 if (self.health <= 0)
1065 self.health = self.max_health;
1066 self.takedamage = DAMAGE_NO; // wil be reset upon return
1082 if(other.classname != "player")
1084 if (self.owner.attack_finished_single > time)
1087 self.owner.attack_finished_single = time + 2;
1089 if (!(self.owner.dmg) && (self.owner.message != ""))
1091 if (other.flags & FL_CLIENT)
1092 centerprint (other, self.owner.message);
1093 play2(other, "misc/talk.wav");
1098 void door_generic_plat_blocked()
1101 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
1102 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1105 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
1106 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1108 //Dont chamge direction for dead or dying stuff
1109 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
1112 if (self.state == STATE_DOWN)
1113 door_rotating_go_up ();
1115 door_rotating_go_down ();
1118 //gib dying stuff just to make sure
1119 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
1120 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
1124 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1125 // if a door has a negative wait, it would never come back if blocked,
1126 // so let it just squash the object to death real fast
1127 /* if (self.wait >= 0)
1129 if (self.state == STATE_DOWN)
1130 door_rotating_go_up ();
1132 door_rotating_go_down ();
1138 void door_rotating_hit_top()
1140 if (self.noise1 != "")
1141 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1142 self.state = STATE_TOP;
1143 if (self.spawnflags & DOOR_TOGGLE)
1144 return; // don't come down automatically
1145 self.think = door_rotating_go_down;
1146 self.nextthink = self.ltime + self.wait;
1149 void door_rotating_hit_bottom()
1151 if (self.noise1 != "")
1152 sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1153 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
1155 self.pos2 = '0 0 0' - self.pos2;
1158 self.state = STATE_BOTTOM;
1161 void door_rotating_go_down()
1163 if (self.noise2 != "")
1164 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1165 if (self.max_health)
1167 self.takedamage = DAMAGE_YES;
1168 self.health = self.max_health;
1171 self.state = STATE_DOWN;
1172 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
1175 void door_rotating_go_up()
1177 if (self.state == STATE_UP)
1178 return; // already going up
1180 if (self.state == STATE_TOP)
1181 { // reset top wait time
1182 self.nextthink = self.ltime + self.wait;
1185 if (self.noise2 != "")
1186 sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1187 self.state = STATE_UP;
1188 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
1191 oldmessage = self.message;
1194 self.message = oldmessage;
1201 =============================================================================
1205 =============================================================================
1209 entity spawn_field(vector fmins, vector fmaxs)
1215 trigger.classname = "doortriggerfield";
1216 trigger.movetype = MOVETYPE_NONE;
1217 trigger.solid = SOLID_TRIGGER;
1218 trigger.owner = self;
1219 trigger.touch = door_trigger_touch;
1223 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1228 entity LinkDoors_nextent(entity cur, entity near, entity pass)
1230 while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
1236 float LinkDoors_isconnected(entity e1, entity e2, entity pass)
1239 if (e1.absmin_x > e2.absmax_x + DELTA)
1241 if (e1.absmin_y > e2.absmax_y + DELTA)
1243 if (e1.absmin_z > e2.absmax_z + DELTA)
1245 if (e2.absmin_x > e1.absmax_x + DELTA)
1247 if (e2.absmin_y > e1.absmax_y + DELTA)
1249 if (e2.absmin_z > e1.absmax_z + DELTA)
1264 vector cmins, cmaxs;
1267 return; // already linked by another door
1268 if (self.spawnflags & 4)
1270 self.owner = self.enemy = self;
1278 self.trigger_field = spawn_field(self.absmin, self.absmax);
1280 return; // don't want to link this door
1283 FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
1285 // set owner, and make a loop of the chain
1286 dprint("LinkDoors: linking doors:");
1287 for(t = self; ; t = t.enemy)
1289 dprint(" ", etos(t));
1291 if(t.enemy == world)
1299 // collect health, targetname, message, size
1300 cmins = self.absmin;
1301 cmaxs = self.absmax;
1302 for(t = self; ; t = t.enemy)
1304 if(t.health && !self.health)
1305 self.health = t.health;
1306 if((t.targetname != "") && (self.targetname == ""))
1307 self.targetname = t.targetname;
1308 if((t.message != "") && (self.message == ""))
1309 self.message = t.message;
1310 if (t.absmin_x < cmins_x)
1311 cmins_x = t.absmin_x;
1312 if (t.absmin_y < cmins_y)
1313 cmins_y = t.absmin_y;
1314 if (t.absmin_z < cmins_z)
1315 cmins_z = t.absmin_z;
1316 if (t.absmax_x > cmaxs_x)
1317 cmaxs_x = t.absmax_x;
1318 if (t.absmax_y > cmaxs_y)
1319 cmaxs_y = t.absmax_y;
1320 if (t.absmax_z > cmaxs_z)
1321 cmaxs_z = t.absmax_z;
1326 // distribute health, targetname, message
1327 for(t = self; t; t = t.enemy)
1329 t.health = self.health;
1330 t.targetname = self.targetname;
1331 t.message = self.message;
1336 // shootable, or triggered doors just needed the owner/enemy links,
1337 // they don't spawn a field
1346 self.trigger_field = spawn_field(cmins, cmaxs);
1350 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
1351 if two doors touch, they are assumed to be connected and operate as a unit.
1353 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1355 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).
1357 GOLD_KEY causes the door to open only if the activator holds a gold key.
1359 SILVER_KEY causes the door to open only if the activator holds a silver key.
1361 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1362 "angle" determines the opening direction
1363 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1364 "health" if set, door must be shot open
1365 "speed" movement speed (100 default)
1366 "wait" wait before returning (3 default, -1 = never return)
1367 "lip" lip remaining at end of move (8 default)
1368 "dmg" damage to inflict when blocked (2 default)
1375 FIXME: only one sound set available at the time being
1379 void door_init_startopen()
1381 setorigin (self, self.pos2);
1382 self.pos2 = self.pos1;
1383 self.pos1 = self.origin;
1388 setorigin(self, self.pos1);
1389 self.velocity = '0 0 0';
1390 self.state = STATE_BOTTOM;
1391 self.think = func_null;
1395 // spawnflags require key (for now only func_door)
1396 #define SPAWNFLAGS_GOLD_KEY 8
1397 #define SPAWNFLAGS_SILVER_KEY 16
1398 void spawnfunc_func_door()
1400 // Quake 1 keys compatibility
1401 if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
1402 self.itemkeys |= ITEM_KEY_BIT(0);
1403 if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
1404 self.itemkeys |= ITEM_KEY_BIT(1);
1406 //if (!self.deathtype) // map makers can override this
1407 // self.deathtype = " got in the way";
1410 self.max_health = self.health;
1411 if not(InitMovingBrushTrigger())
1413 self.effects |= EF_LOWPRECISION;
1414 self.classname = "door";
1416 self.blocked = door_blocked;
1417 self.use = door_use;
1419 // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
1420 // if(self.spawnflags & 8)
1421 // self.dmg = 10000;
1423 if(self.dmg && (self.message == ""))
1424 self.message = "was squished";
1425 if(self.dmg && (self.message2 == ""))
1426 self.message2 = "was squished by";
1428 if (self.sounds > 0)
1430 precache_sound ("plats/medplat1.wav");
1431 precache_sound ("plats/medplat2.wav");
1432 self.noise2 = "plats/medplat1.wav";
1433 self.noise1 = "plats/medplat2.wav";
1443 self.pos1 = self.origin;
1444 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1446 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1447 // but spawn in the open position
1448 if (self.spawnflags & DOOR_START_OPEN)
1449 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1451 self.state = STATE_BOTTOM;
1455 self.takedamage = DAMAGE_YES;
1456 self.event_damage = door_damage;
1462 self.touch = door_touch;
1464 // LinkDoors can't be done until all of the doors have been spawned, so
1465 // the sizes can be detected properly.
1466 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1468 self.reset = door_reset;
1471 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1472 if two doors touch, they are assumed to be connected and operate as a unit.
1474 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1476 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1477 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1478 must have set trigger_reverse to 1.
1479 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1481 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).
1483 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1484 "angle" determines the destination angle for opening. negative values reverse the direction.
1485 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1486 "health" if set, door must be shot open
1487 "speed" movement speed (100 default)
1488 "wait" wait before returning (3 default, -1 = never return)
1489 "dmg" damage to inflict when blocked (2 default)
1496 FIXME: only one sound set available at the time being
1499 void door_rotating_reset()
1501 self.angles = self.pos1;
1502 self.avelocity = '0 0 0';
1503 self.state = STATE_BOTTOM;
1504 self.think = func_null;
1508 void door_rotating_init_startopen()
1510 self.angles = self.movedir;
1511 self.pos2 = '0 0 0';
1512 self.pos1 = self.movedir;
1516 void spawnfunc_func_door_rotating()
1519 //if (!self.deathtype) // map makers can override this
1520 // self.deathtype = " got in the way";
1522 // I abuse "movedir" for denoting the axis for now
1523 if (self.spawnflags & 64) // X (untested)
1524 self.movedir = '0 0 1';
1525 else if (self.spawnflags & 128) // Y (untested)
1526 self.movedir = '1 0 0';
1528 self.movedir = '0 1 0';
1530 if (self.angles_y==0) self.angles_y = 90;
1532 self.movedir = self.movedir * self.angles_y;
1533 self.angles = '0 0 0';
1535 self.max_health = self.health;
1536 self.avelocity = self.movedir;
1537 if not(InitMovingBrushTrigger())
1539 self.velocity = '0 0 0';
1540 //self.effects |= EF_LOWPRECISION;
1541 self.classname = "door_rotating";
1543 self.blocked = door_blocked;
1544 self.use = door_use;
1546 if(self.spawnflags & 8)
1549 if(self.dmg && (self.message == ""))
1550 self.message = "was squished";
1551 if(self.dmg && (self.message2 == ""))
1552 self.message2 = "was squished by";
1554 if (self.sounds > 0)
1556 precache_sound ("plats/medplat1.wav");
1557 precache_sound ("plats/medplat2.wav");
1558 self.noise2 = "plats/medplat1.wav";
1559 self.noise1 = "plats/medplat2.wav";
1566 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1568 self.pos1 = '0 0 0';
1569 self.pos2 = self.movedir;
1571 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1572 // but spawn in the open position
1573 if (self.spawnflags & DOOR_START_OPEN)
1574 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1576 self.state = STATE_BOTTOM;
1580 self.takedamage = DAMAGE_YES;
1581 self.event_damage = door_damage;
1587 self.touch = door_touch;
1589 // LinkDoors can't be done until all of the doors have been spawned, so
1590 // the sizes can be detected properly.
1591 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1593 self.reset = door_rotating_reset;
1597 =============================================================================
1601 =============================================================================
1604 void() fd_secret_move1;
1605 void() fd_secret_move2;
1606 void() fd_secret_move3;
1607 void() fd_secret_move4;
1608 void() fd_secret_move5;
1609 void() fd_secret_move6;
1610 void() fd_secret_done;
1612 float SECRET_OPEN_ONCE = 1; // stays open
1613 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1614 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1615 float SECRET_NO_SHOOT = 8; // only opened by trigger
1616 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1618 void fd_secret_use()
1621 string message_save;
1623 self.health = 10000;
1624 self.bot_attack = TRUE;
1626 // exit if still moving around...
1627 if (self.origin != self.oldorigin)
1630 message_save = self.message;
1631 self.message = ""; // no more message
1632 SUB_UseTargets(); // fire all targets / killtargets
1633 self.message = message_save;
1635 self.velocity = '0 0 0';
1637 // Make a sound, wait a little...
1639 if (self.noise1 != "")
1640 sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
1641 self.nextthink = self.ltime + 0.1;
1643 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1644 makevectors(self.mangle);
1648 if (self.spawnflags & SECRET_1ST_DOWN)
1649 self.t_width = fabs(v_up * self.size);
1651 self.t_width = fabs(v_right * self.size);
1655 self.t_length = fabs(v_forward * self.size);
1657 if (self.spawnflags & SECRET_1ST_DOWN)
1658 self.dest1 = self.origin - v_up * self.t_width;
1660 self.dest1 = self.origin + v_right * (self.t_width * temp);
1662 self.dest2 = self.dest1 + v_forward * self.t_length;
1663 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1664 if (self.noise2 != "")
1665 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1668 void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
1673 // Wait after first movement...
1674 void fd_secret_move1()
1676 self.nextthink = self.ltime + 1.0;
1677 self.think = fd_secret_move2;
1678 if (self.noise3 != "")
1679 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1682 // Start moving sideways w/sound...
1683 void fd_secret_move2()
1685 if (self.noise2 != "")
1686 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1687 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1690 // Wait here until time to go back...
1691 void fd_secret_move3()
1693 if (self.noise3 != "")
1694 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1695 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1697 self.nextthink = self.ltime + self.wait;
1698 self.think = fd_secret_move4;
1703 void fd_secret_move4()
1705 if (self.noise2 != "")
1706 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1707 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1711 void fd_secret_move5()
1713 self.nextthink = self.ltime + 1.0;
1714 self.think = fd_secret_move6;
1715 if (self.noise3 != "")
1716 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1719 void fd_secret_move6()
1721 if (self.noise2 != "")
1722 sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
1723 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1726 void fd_secret_done()
1728 if (self.spawnflags&SECRET_YES_SHOOT)
1730 self.health = 10000;
1731 self.takedamage = DAMAGE_YES;
1732 //self.th_pain = fd_secret_use;
1734 if (self.noise3 != "")
1735 sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
1738 void secret_blocked()
1740 if (time < self.attack_finished_single)
1742 self.attack_finished_single = time + 0.5;
1743 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1755 if not(other.iscreature)
1757 if (self.attack_finished_single > time)
1760 self.attack_finished_single = time + 2;
1764 if (other.flags & FL_CLIENT)
1765 centerprint (other, self.message);
1766 play2(other, "misc/talk.wav");
1772 if (self.spawnflags&SECRET_YES_SHOOT)
1774 self.health = 10000;
1775 self.takedamage = DAMAGE_YES;
1777 setorigin(self, self.oldorigin);
1778 self.think = func_null;
1782 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1783 Basic secret door. Slides back, then to the side. Angle determines direction.
1784 wait = # of seconds before coming back
1785 1st_left = 1st move is left of arrow
1786 1st_down = 1st move is down from arrow
1787 always_shoot = even if targeted, keep shootable
1788 t_width = override WIDTH to move back (or height if going down)
1789 t_length = override LENGTH to move sideways
1790 "dmg" damage to inflict when blocked (2 default)
1792 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1799 void spawnfunc_func_door_secret()
1801 /*if (!self.deathtype) // map makers can override this
1802 self.deathtype = " got in the way";*/
1808 self.mangle = self.angles;
1809 self.angles = '0 0 0';
1810 self.classname = "door";
1811 if not(InitMovingBrushTrigger())
1813 self.effects |= EF_LOWPRECISION;
1815 self.touch = secret_touch;
1816 self.blocked = secret_blocked;
1818 self.use = fd_secret_use;
1823 self.spawnflags |= SECRET_YES_SHOOT;
1825 if(self.spawnflags&SECRET_YES_SHOOT)
1827 self.health = 10000;
1828 self.takedamage = DAMAGE_YES;
1829 self.event_damage = fd_secret_damage;
1831 self.oldorigin = self.origin;
1833 self.wait = 5; // 5 seconds before closing
1835 self.reset = secret_reset;
1839 /*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
1840 Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
1841 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
1842 speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
1843 height: amplitude modifier (default 32)
1844 phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
1845 noise: path/name of looping .wav file to play.
1846 dmg: Do this mutch dmg every .dmgtime intervall when blocked
1850 void func_fourier_controller_think()
1855 self.nextthink = time + 0.1;
1856 if not (self.owner.active == ACTIVE_ACTIVE)
1858 self.owner.velocity = '0 0 0';
1863 n = floor((tokenize_console(self.owner.netname)) / 5);
1864 t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
1866 v = self.owner.destvec;
1868 for(i = 0; i < n; ++i)
1870 makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
1871 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;
1874 if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
1875 // * 10 so it will arrive in 0.1 sec
1876 self.owner.velocity = (v - self.owner.origin) * 10;
1879 void spawnfunc_func_fourier()
1882 if (self.noise != "")
1884 precache_sound(self.noise);
1885 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
1892 self.destvec = self.origin;
1893 self.cnt = 360 / self.speed;
1895 self.blocked = generic_plat_blocked;
1896 if(self.dmg && (self.message == ""))
1897 self.message = " was squished";
1898 if(self.dmg && (self.message2 == ""))
1899 self.message2 = "was squished by";
1900 if(self.dmg && (!self.dmgtime))
1901 self.dmgtime = 0.25;
1902 self.dmgtime2 = time;
1904 if(self.netname == "")
1905 self.netname = "1 0 0 0 1";
1907 if not(InitMovingBrushTrigger())
1910 self.active = ACTIVE_ACTIVE;
1912 // wait for targets to spawn
1913 controller = spawn();
1914 controller.classname = "func_fourier_controller";
1915 controller.owner = self;
1916 controller.nextthink = time + 1;
1917 controller.think = func_fourier_controller_think;
1918 self.nextthink = self.ltime + 999999999;
1919 self.think = SUB_NullThink; // for PushMove
1921 // Savage: Reduce bandwith, critical on e.g. nexdm02
1922 self.effects |= EF_LOWPRECISION;
1924 // TODO make a reset function for this one
1927 // reusing some fields havocbots declared
1928 .entity wp00, wp01, wp02, wp03;
1930 .float targetfactor, target2factor, target3factor, target4factor;
1931 .vector targetnormal, target2normal, target3normal, target4normal;
1933 vector func_vectormamamam_origin(entity o, float t)
1945 p = e.origin + t * e.velocity;
1947 v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
1949 v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
1955 p = e.origin + t * e.velocity;
1957 v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
1959 v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
1965 p = e.origin + t * e.velocity;
1967 v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
1969 v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
1975 p = e.origin + t * e.velocity;
1977 v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
1979 v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
1985 void func_vectormamamam_controller_think()
1987 self.nextthink = time + 0.1;
1989 if not (self.owner.active == ACTIVE_ACTIVE)
1991 self.owner.velocity = '0 0 0';
1995 if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
1996 self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
1999 void func_vectormamamam_findtarget()
2001 if(self.target != "")
2002 self.wp00 = find(world, targetname, self.target);
2004 if(self.target2 != "")
2005 self.wp01 = find(world, targetname, self.target2);
2007 if(self.target3 != "")
2008 self.wp02 = find(world, targetname, self.target3);
2010 if(self.target4 != "")
2011 self.wp03 = find(world, targetname, self.target4);
2013 if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
2014 objerror("No reference entity found, so there is nothing to move. Aborting.");
2016 self.destvec = self.origin - func_vectormamamam_origin(self, 0);
2019 controller = spawn();
2020 controller.classname = "func_vectormamamam_controller";
2021 controller.owner = self;
2022 controller.nextthink = time + 1;
2023 controller.think = func_vectormamamam_controller_think;
2026 void spawnfunc_func_vectormamamam()
2028 if (self.noise != "")
2030 precache_sound(self.noise);
2031 soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
2034 if(!self.targetfactor)
2035 self.targetfactor = 1;
2037 if(!self.target2factor)
2038 self.target2factor = 1;
2040 if(!self.target3factor)
2041 self.target3factor = 1;
2043 if(!self.target4factor)
2044 self.target4factor = 1;
2046 if(vlen(self.targetnormal))
2047 self.targetnormal = normalize(self.targetnormal);
2049 if(vlen(self.target2normal))
2050 self.target2normal = normalize(self.target2normal);
2052 if(vlen(self.target3normal))
2053 self.target3normal = normalize(self.target3normal);
2055 if(vlen(self.target4normal))
2056 self.target4normal = normalize(self.target4normal);
2058 self.blocked = generic_plat_blocked;
2059 if(self.dmg && (self.message == ""))
2060 self.message = " was squished";
2061 if(self.dmg && (self.message == ""))
2062 self.message2 = "was squished by";
2063 if(self.dmg && (!self.dmgtime))
2064 self.dmgtime = 0.25;
2065 self.dmgtime2 = time;
2067 if(self.netname == "")
2068 self.netname = "1 0 0 0 1";
2070 if not(InitMovingBrushTrigger())
2073 // wait for targets to spawn
2074 self.nextthink = self.ltime + 999999999;
2075 self.think = SUB_NullThink; // for PushMove
2077 // Savage: Reduce bandwith, critical on e.g. nexdm02
2078 self.effects |= EF_LOWPRECISION;
2080 self.active = ACTIVE_ACTIVE;
2082 InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
2085 void conveyor_think()
2089 // set myself as current conveyor where possible
2090 for(e = world; (e = findentity(e, conveyor, self)); )
2095 for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
2096 if(!e.conveyor.state)
2099 vector emin = e.absmin;
2100 vector emax = e.absmax;
2101 if(self.solid == SOLID_BSP)
2106 if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
2107 if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
2111 for(e = world; (e = findentity(e, conveyor, self)); )
2113 if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
2114 continue; // done in SV_PlayerPhysics
2116 setorigin(e, e.origin + self.movedir * sys_frametime);
2117 move_out_of_solid(e);
2118 UpdateCSQCProjectile(e);
2120 // stupid conveyor code
2121 tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
2122 if(trace_fraction > 0)
2123 setorigin(e, trace_endpos);
2128 self.nextthink = time;
2133 self.state = !self.state;
2136 void conveyor_reset()
2138 self.state = (self.spawnflags & 1);
2141 void conveyor_init()
2145 self.movedir = self.movedir * self.speed;
2146 self.think = conveyor_think;
2147 self.nextthink = time;
2150 self.use = conveyor_use;
2151 self.reset = conveyor_reset;
2158 void spawnfunc_trigger_conveyor()
2165 void spawnfunc_func_conveyor()
2168 InitMovingBrushTrigger();
2169 self.movetype = MOVETYPE_NONE;