3 float STATE_BOTTOM = 1;
11 void() plat_center_touch;
12 void() plat_outside_touch;
13 void() plat_trigger_use;
17 float PLAT_LOW_TRIGGER = 1;
19 void plat_spawn_inside_trigger()
22 local vector tmin, tmax;
25 trigger.touch = plat_center_touch;
26 trigger.movetype = MOVETYPE_NONE;
27 trigger.solid = SOLID_TRIGGER;
30 tmin = self.absmin + '25 25 0';
31 tmax = self.absmax - '25 25 -8';
32 tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
33 if (self.spawnflags & PLAT_LOW_TRIGGER)
36 if (self.size_x <= 50)
38 tmin_x = (self.mins_x + self.maxs_x) / 2;
41 if (self.size_y <= 50)
43 tmin_y = (self.mins_y + self.maxs_y) / 2;
47 setsize (trigger, tmin, tmax);
52 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
54 self.think = plat_go_down;
55 self.nextthink = self.ltime + 3;
58 void plat_hit_bottom()
60 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
66 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
68 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
73 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
75 SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
78 void plat_center_touch()
80 if not(other.iscreature)
83 if (other.health <= 0)
89 else if (self.state == 1)
90 self.nextthink = self.ltime + 1; // delay going down
93 void plat_outside_touch()
95 if not(other.iscreature)
98 if (other.health <= 0)
106 void plat_trigger_use()
109 return; // already activated
116 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
117 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
119 if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite?
120 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
121 // Gib dead/dying stuff
122 if(other.deadflag != DEAD_NO)
123 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
128 else if (self.state == 3)
131 objerror ("plat_crush: bad self.state\n");
139 objerror ("plat_use: not in up state");
143 void plat_init_movedown()
145 setorigin (self, self.pos2);
149 .string sound1, sound2;
151 void spawnfunc_path_corner() { };
152 void spawnfunc_func_plat()
159 if (self.sounds == 0)
162 if(self.spawnflags & 4)
165 if(self.dmg && (!self.message))
166 self.message = "was squished";
167 if(self.dmg && (!self.message2))
168 self.message2 = "was squished by";
170 if (self.sounds == 1)
172 precache_sound ("plats/plat1.wav");
173 precache_sound ("plats/plat2.wav");
174 self.noise = "plats/plat1.wav";
175 self.noise1 = "plats/plat2.wav";
178 if (self.sounds == 2)
180 precache_sound ("plats/medplat1.wav");
181 precache_sound ("plats/medplat2.wav");
182 self.noise = "plats/medplat1.wav";
183 self.noise1 = "plats/medplat2.wav";
188 precache_sound (self.sound1);
189 self.noise = self.sound1;
193 precache_sound (self.sound2);
194 self.noise1 = self.sound2;
197 self.mangle = self.angles;
198 self.angles = '0 0 0';
200 self.classname = "plat";
201 InitMovingBrushTrigger();
202 self.effects |= EF_LOWPRECISION;
203 setsize (self, self.mins , self.maxs);
205 self.blocked = plat_crush;
210 self.pos1 = self.origin;
211 self.pos2 = self.origin;
212 self.pos2_z = self.origin_z - self.size_z + 8;
214 self.use = plat_trigger_use;
216 plat_spawn_inside_trigger (); // the "start moving" trigger
224 InitializeEntity(self, plat_init_movedown, INITPRIO_SETLOCATION);
231 self.think = train_next;
232 self.nextthink = self.ltime + self.wait;
238 targ = find(world, targetname, self.target);
239 self.target = targ.target;
241 objerror("train_next: no next target");
242 self.wait = targ.wait;
248 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
250 SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
255 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
257 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
261 void func_train_find()
264 targ = find(world, targetname, self.target);
265 self.target = targ.target;
267 objerror("func_train_find: no next target");
268 setorigin(self, targ.origin - self.mins);
269 self.nextthink = self.ltime + 1;
270 self.think = train_next;
273 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
274 Ridable platform, targets spawnfunc_path_corner path to follow.
275 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
276 target : targetname of first spawnfunc_path_corner (starts here)
278 void spawnfunc_func_train()
281 objerror("func_train without a target");
285 InitMovingBrushTrigger();
286 self.effects |= EF_LOWPRECISION;
288 // wait for targets to spawn
289 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
294 void rotating_blocked()
297 if(self.dmg && other.takedamage != DAMAGE_NO) {
298 if(self.dmgtime2 < time) {
299 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
300 self.dmgtime2 = time + self.dmgtime;
303 // Gib dead/dying stuff
304 if(other.deadflag != DEAD_NO)
305 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
311 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
312 Brush model that spins in place on one axis (default Z).
313 speed : speed to rotate (in degrees per second)
314 noise : path/name of looping .wav file to play.
315 dmg : Do this mutch dmg every .dmgtime intervall when blocked
319 void spawnfunc_func_rotating()
323 precache_sound(self.noise);
324 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
328 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
329 if (self.spawnflags & 4) // X (untested)
330 self.avelocity = '0 0 1' * self.speed;
331 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
332 else if (self.spawnflags & 8) // Y (untested)
333 self.avelocity = '1 0 0' * self.speed;
334 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
336 self.avelocity = '0 1 0' * self.speed;
338 if(self.dmg & (!self.message))
339 self.message = " was squished";
340 if(self.dmg && (!self.message2))
341 self.message2 = "was squished by";
344 if(self.dmg && (!self.dmgtime))
347 self.dmgtime2 = time;
349 InitMovingBrushTrigger();
350 // no EF_LOWPRECISION here, as rounding angles is bad
352 self.blocked = rotating_blocked;
354 // wait for targets to spawn
355 self.nextthink = self.ltime + 999999999;
356 self.think = SUB_Null;
360 void func_bobbing_controller_think()
363 self.nextthink = time + 0.1;
364 // calculate sinewave using makevectors
365 makevectors((time * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
366 v = self.owner.destvec + self.owner.movedir * v_forward_y;
367 // * 10 so it will arrive in 0.1 sec
368 self.owner.velocity = (v - self.owner.origin) * 10;
371 void bobbing_blocked()
373 // no need to duplicate code
377 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
378 Brush model that moves back and forth on one axis (default Z).
379 speed : how long one cycle takes in seconds (default 4)
380 height : how far the cycle moves (default 32)
381 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
382 noise : path/name of looping .wav file to play.
383 dmg : Do this mutch dmg every .dmgtime intervall when blocked
386 void spawnfunc_func_bobbing()
388 local entity controller;
391 precache_sound(self.noise);
392 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
398 // center of bobbing motion
399 self.destvec = self.origin;
400 // time scale to get degrees
401 self.cnt = 360 / self.speed;
403 // damage when blocked
404 self.blocked = bobbing_blocked;
405 if(self.dmg & (!self.message))
406 self.message = " was squished";
407 if(self.dmg && (!self.message2))
408 self.message2 = "was squished by";
409 if(self.dmg && (!self.dmgtime))
411 self.dmgtime2 = time;
414 if (self.spawnflags & 1) // X
415 self.movedir = '1 0 0' * self.height;
416 else if (self.spawnflags & 2) // Y
417 self.movedir = '0 1 0' * self.height;
419 self.movedir = '0 0 1' * self.height;
421 InitMovingBrushTrigger();
423 // wait for targets to spawn
424 controller = spawn();
425 controller.classname = "func_bobbing_controller";
426 controller.owner = self;
427 controller.nextthink = time + 1;
428 controller.think = func_bobbing_controller_think;
429 self.nextthink = self.ltime + 999999999;
430 self.think = SUB_Null;
432 // Savage: Reduce bandwith, critical on e.g. nexdm02
433 self.effects |= EF_LOWPRECISION;
436 // button and multiple button
439 void() button_return;
443 self.state = STATE_TOP;
444 self.nextthink = self.ltime + self.wait;
445 self.think = button_return;
446 activator = self.enemy;
448 self.frame = 1; // use alternate textures
453 self.state = STATE_BOTTOM;
458 self.state = STATE_DOWN;
459 SUB_CalcMove (self.pos1, self.speed, button_done);
460 self.frame = 0; // use normal textures
462 self.takedamage = DAMAGE_YES; // can be shot again
466 void button_blocked()
468 // do nothing, just don't come all the way back out
474 self.health = self.max_health;
475 self.takedamage = DAMAGE_NO; // will be reset upon return
477 if (self.state == STATE_UP || self.state == STATE_TOP)
480 if (self.noise != "")
481 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
483 self.state = STATE_UP;
484 SUB_CalcMove (self.pos2, self.speed, button_wait);
490 // if (activator.classname != "player")
492 // dprint(activator.classname);
493 // dprint(" triggered a button\n");
495 self.enemy = activator;
501 // if (activator.classname != "player")
503 // dprint(activator.classname);
504 // dprint(" touched a button\n");
508 if not(other.iscreature)
510 if(other.velocity * self.movedir < 0)
514 self.enemy = other.owner;
518 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
520 self.health = self.health - damage;
521 if (self.health <= 0)
523 // if (activator.classname != "player")
525 // dprint(activator.classname);
526 // dprint(" killed a button\n");
528 self.enemy = damage_attacker;
534 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
535 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.
537 "angle" determines the opening direction
538 "target" all entities with a matching targetname will be used
539 "speed" override the default 40 speed
540 "wait" override the default 1 second wait (-1 = never return)
541 "lip" override the default 4 pixel lip remaining at end of move
542 "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
549 void spawnfunc_func_button()
553 InitMovingBrushTrigger();
554 self.effects |= EF_LOWPRECISION;
556 self.blocked = button_blocked;
557 self.use = button_use;
559 // if (self.health == 0) // all buttons are now shootable
563 self.max_health = self.health;
564 self.event_damage = button_damage;
565 self.takedamage = DAMAGE_YES;
568 self.touch = button_touch;
578 precache_sound(self.noise);
580 self.state = STATE_BOTTOM;
582 self.pos1 = self.origin;
583 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
587 float DOOR_START_OPEN = 1;
588 float DOOR_DONT_LINK = 4;
589 float DOOR_TOGGLE = 32;
593 Doors are similar to buttons, but can spawn a fat trigger field around them
594 to open without a touch, and they link together to form simultanious
597 Door.owner is the master door. If there is only one door, it points to itself.
598 If multiple doors, all will point to a single one.
600 Door.enemy chains from the master door through all doors linked in the chain.
605 =============================================================================
609 =============================================================================
614 void() door_rotating_go_down;
615 void() door_rotating_go_up;
620 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
621 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
624 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
625 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
627 //Dont chamge direction for dead or dying stuff
628 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
631 if (self.state == STATE_DOWN)
632 if (self.classname == "door")
637 door_rotating_go_up ();
640 if (self.classname == "door")
645 door_rotating_go_down ();
649 //gib dying stuff just to make sure
650 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
651 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
655 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
656 // if a door has a negative wait, it would never come back if blocked,
657 // so let it just squash the object to death real fast
658 /* if (self.wait >= 0)
660 if (self.state == STATE_DOWN)
671 if (self.noise1 != "")
672 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
673 self.state = STATE_TOP;
674 if (self.spawnflags & DOOR_TOGGLE)
675 return; // don't come down automatically
676 if (self.classname == "door")
678 self.think = door_go_down;
681 self.think = door_rotating_go_down;
683 self.nextthink = self.ltime + self.wait;
686 void door_hit_bottom()
688 if (self.noise1 != "")
689 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
690 self.state = STATE_BOTTOM;
695 if (self.noise2 != "")
696 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
699 self.takedamage = DAMAGE_YES;
700 self.health = self.max_health;
703 self.state = STATE_DOWN;
704 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
709 if (self.state == STATE_UP)
710 return; // already going up
712 if (self.state == STATE_TOP)
713 { // reset top wait time
714 self.nextthink = self.ltime + self.wait;
718 if (self.noise2 != "")
719 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
720 self.state = STATE_UP;
721 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
724 oldmessage = self.message;
727 self.message = oldmessage;
732 =============================================================================
736 =============================================================================
744 if (self.owner != self)
745 objerror ("door_fire: self.owner != self");
749 if (self.spawnflags & DOOR_TOGGLE)
751 if (self.state == STATE_UP || self.state == STATE_TOP)
756 if (self.classname == "door")
762 door_rotating_go_down ();
765 } while ( (self != starte) && (self != world) );
771 // trigger all paired doors
775 if (self.classname == "door")
780 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
781 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
783 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
784 self.pos2 = '0 0 0' - self.pos2;
786 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
787 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
788 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
790 door_rotating_go_up ();
794 } while ( (self != starte) && (self != world) );
803 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
814 void door_trigger_touch()
816 if (other.health < 1)
817 if not(other.iscreature && other.deadflag == DEAD_NO)
820 if (time < self.attack_finished_single)
822 self.attack_finished_single = time + 1;
831 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
834 self.health = self.health - damage;
835 if (self.health <= 0)
839 self.health = self.max_health;
840 self.takedamage = DAMAGE_NO; // wil be reset upon return
856 if(other.classname != "player")
858 if (self.owner.attack_finished_single > time)
861 self.owner.attack_finished_single = time + 2;
863 if (!(self.owner.dmg) && (self.owner.message != ""))
865 if (other.flags & FL_CLIENT)
866 centerprint (other, self.owner.message);
867 play2(other, "misc/talk.wav");
872 void door_rotating_blocked()
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 door_rotating_go_up ();
889 door_rotating_go_down ();
892 //gib dying stuff just to make sure
893 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
894 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
898 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
899 // if a door has a negative wait, it would never come back if blocked,
900 // so let it just squash the object to death real fast
901 /* if (self.wait >= 0)
903 if (self.state == STATE_DOWN)
904 door_rotating_go_up ();
906 door_rotating_go_down ();
912 void door_rotating_hit_top()
914 if (self.noise1 != "")
915 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
916 self.state = STATE_TOP;
917 if (self.spawnflags & DOOR_TOGGLE)
918 return; // don't come down automatically
919 self.think = door_rotating_go_down;
920 self.nextthink = self.ltime + self.wait;
923 void door_rotating_hit_bottom()
925 if (self.noise1 != "")
926 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
927 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
929 self.pos2 = '0 0 0' - self.pos2;
932 self.state = STATE_BOTTOM;
935 void door_rotating_go_down()
937 if (self.noise2 != "")
938 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
941 self.takedamage = DAMAGE_YES;
942 self.health = self.max_health;
945 self.state = STATE_DOWN;
946 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
949 void door_rotating_go_up()
951 if (self.state == STATE_UP)
952 return; // already going up
954 if (self.state == STATE_TOP)
955 { // reset top wait time
956 self.nextthink = self.ltime + self.wait;
959 if (self.noise2 != "")
960 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
961 self.state = STATE_UP;
962 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
965 oldmessage = self.message;
968 self.message = oldmessage;
975 =============================================================================
979 =============================================================================
983 entity spawn_field(vector fmins, vector fmaxs)
985 local entity trigger;
989 trigger.classname = "doortriggerfield";
990 trigger.movetype = MOVETYPE_NONE;
991 trigger.solid = SOLID_TRIGGER;
992 trigger.owner = self;
993 trigger.touch = door_trigger_touch;
997 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1002 float EntitiesTouching(entity e1, entity e2)
1004 if (e1.absmin_x > e2.absmax_x)
1006 if (e1.absmin_y > e2.absmax_y)
1008 if (e1.absmin_z > e2.absmax_z)
1010 if (e1.absmax_x < e2.absmin_x)
1012 if (e1.absmax_y < e2.absmin_y)
1014 if (e1.absmax_z < e2.absmin_z)
1029 local entity t, starte;
1030 local vector cmins, cmaxs;
1033 return; // already linked by another door
1034 if (self.spawnflags & 4)
1036 self.owner = self.enemy = self;
1044 self.trigger_field = spawn_field(self.absmin, self.absmax);
1046 return; // don't want to link this door
1049 cmins = self.absmin;
1050 cmaxs = self.absmax;
1057 self.owner = starte; // master door
1060 starte.health = self.health;
1062 starte.targetname = self.targetname;
1063 if (self.message != "")
1064 starte.message = self.message;
1066 t = find(t, classname, self.classname);
1069 self.enemy = starte; // make the chain a loop
1071 // shootable, or triggered doors just needed the owner/enemy links,
1072 // they don't spawn a field
1083 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1088 if (EntitiesTouching(self,t))
1091 objerror ("cross connected doors");
1096 if (t.absmin_x < cmins_x)
1097 cmins_x = t.absmin_x;
1098 if (t.absmin_y < cmins_y)
1099 cmins_y = t.absmin_y;
1100 if (t.absmin_z < cmins_z)
1101 cmins_z = t.absmin_z;
1102 if (t.absmax_x > cmaxs_x)
1103 cmaxs_x = t.absmax_x;
1104 if (t.absmax_y > cmaxs_y)
1105 cmaxs_y = t.absmax_y;
1106 if (t.absmax_z > cmaxs_z)
1107 cmaxs_z = t.absmax_z;
1114 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1115 if two doors touch, they are assumed to be connected and operate as a unit.
1117 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1119 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).
1121 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1122 "angle" determines the opening direction
1123 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1124 "health" if set, door must be shot open
1125 "speed" movement speed (100 default)
1126 "wait" wait before returning (3 default, -1 = never return)
1127 "lip" lip remaining at end of move (8 default)
1128 "dmg" damage to inflict when blocked (2 default)
1135 FIXME: only one sound set available at the time being
1139 void door_init_startopen()
1141 setorigin (self, self.pos2);
1142 self.pos2 = self.pos1;
1143 self.pos1 = self.origin;
1146 void spawnfunc_func_door()
1148 //if (!self.deathtype) // map makers can override this
1149 // self.deathtype = " got in the way";
1152 self.max_health = self.health;
1153 InitMovingBrushTrigger();
1154 self.effects |= EF_LOWPRECISION;
1155 self.classname = "door";
1157 self.blocked = door_blocked;
1158 self.use = door_use;
1160 if(self.spawnflags & 8)
1163 if(self.dmg && (!self.message))
1164 self.message = "was squished";
1165 if(self.dmg && (!self.message2))
1166 self.message2 = "was squished by";
1168 if (self.sounds > 0)
1170 precache_sound ("plats/medplat1.wav");
1171 precache_sound ("plats/medplat2.wav");
1172 self.noise2 = "plats/medplat1.wav";
1173 self.noise1 = "plats/medplat2.wav";
1183 self.pos1 = self.origin;
1184 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1186 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1187 // but spawn in the open position
1188 if (self.spawnflags & DOOR_START_OPEN)
1189 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1191 self.state = STATE_BOTTOM;
1195 self.takedamage = DAMAGE_YES;
1196 self.event_damage = door_damage;
1202 self.touch = door_touch;
1204 // LinkDoors can't be done until all of the doors have been spawned, so
1205 // the sizes can be detected properly.
1206 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1209 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1210 if two doors touch, they are assumed to be connected and operate as a unit.
1212 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1214 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1215 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1216 must have set trigger_reverse to 1.
1217 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1219 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).
1221 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1222 "angle" determines the destination angle for opening. negative values reverse the direction.
1223 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1224 "health" if set, door must be shot open
1225 "speed" movement speed (100 default)
1226 "wait" wait before returning (3 default, -1 = never return)
1227 "dmg" damage to inflict when blocked (2 default)
1234 FIXME: only one sound set available at the time being
1237 void door_rotating_init_startopen()
1239 self.angles = self.movedir;
1240 self.pos2 = '0 0 0';
1241 self.pos1 = self.movedir;
1245 void spawnfunc_func_door_rotating()
1248 //if (!self.deathtype) // map makers can override this
1249 // self.deathtype = " got in the way";
1251 // I abuse "movedir" for denoting the axis for now
1252 if (self.spawnflags & 64) // X (untested)
1253 self.movedir = '0 0 1';
1254 else if (self.spawnflags & 128) // Y (untested)
1255 self.movedir = '1 0 0';
1257 self.movedir = '0 1 0';
1259 if (self.angles_y==0) self.angles_y = 90;
1261 self.movedir = self.movedir * self.angles_y;
1262 self.angles = '0 0 0';
1264 self.max_health = self.health;
1265 InitMovingBrushTrigger();
1266 //self.effects |= EF_LOWPRECISION;
1267 self.classname = "door_rotating";
1269 self.blocked = door_blocked;
1270 self.use = door_use;
1272 if(self.spawnflags & 8)
1275 if(self.dmg && (!self.message))
1276 self.message = "was squished";
1277 if(self.dmg && (!self.message2))
1278 self.message2 = "was squished by";
1280 if (self.sounds > 0)
1282 precache_sound ("plats/medplat1.wav");
1283 precache_sound ("plats/medplat2.wav");
1284 self.noise2 = "plats/medplat1.wav";
1285 self.noise1 = "plats/medplat2.wav";
1292 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1294 self.pos1 = '0 0 0';
1295 self.pos2 = self.movedir;
1297 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1298 // but spawn in the open position
1299 if (self.spawnflags & DOOR_START_OPEN)
1300 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1302 self.state = STATE_BOTTOM;
1306 self.takedamage = DAMAGE_YES;
1307 self.event_damage = door_damage;
1313 self.touch = door_touch;
1315 // LinkDoors can't be done until all of the doors have been spawned, so
1316 // the sizes can be detected properly.
1317 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1321 =============================================================================
1325 =============================================================================
1328 void() fd_secret_move1;
1329 void() fd_secret_move2;
1330 void() fd_secret_move3;
1331 void() fd_secret_move4;
1332 void() fd_secret_move5;
1333 void() fd_secret_move6;
1334 void() fd_secret_done;
1336 float SECRET_OPEN_ONCE = 1; // stays open
1337 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1338 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1339 float SECRET_NO_SHOOT = 8; // only opened by trigger
1340 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1343 void fd_secret_use()
1346 string message_save;
1348 self.health = 10000;
1349 self.bot_attack = TRUE;
1351 // exit if still moving around...
1352 if (self.origin != self.oldorigin)
1355 message_save = self.message;
1356 self.message = ""; // no more message
1357 SUB_UseTargets(); // fire all targets / killtargets
1358 self.message = message_save;
1360 self.velocity = '0 0 0';
1362 // Make a sound, wait a little...
1364 if (self.noise1 != "")
1365 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1366 self.nextthink = self.ltime + 0.1;
1368 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1369 makevectors(self.mangle);
1373 if (self.spawnflags & SECRET_1ST_DOWN)
1374 self.t_width = fabs(v_up * self.size);
1376 self.t_width = fabs(v_right * self.size);
1380 self.t_length = fabs(v_forward * self.size);
1382 if (self.spawnflags & SECRET_1ST_DOWN)
1383 self.dest1 = self.origin - v_up * self.t_width;
1385 self.dest1 = self.origin + v_right * (self.t_width * temp);
1387 self.dest2 = self.dest1 + v_forward * self.t_length;
1388 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1389 if (self.noise2 != "")
1390 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1393 // Wait after first movement...
1394 void fd_secret_move1()
1396 self.nextthink = self.ltime + 1.0;
1397 self.think = fd_secret_move2;
1398 if (self.noise3 != "")
1399 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1402 // Start moving sideways w/sound...
1403 void fd_secret_move2()
1405 if (self.noise2 != "")
1406 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1407 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1410 // Wait here until time to go back...
1411 void fd_secret_move3()
1413 if (self.noise3 != "")
1414 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1415 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1417 self.nextthink = self.ltime + self.wait;
1418 self.think = fd_secret_move4;
1423 void fd_secret_move4()
1425 if (self.noise2 != "")
1426 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1427 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1431 void fd_secret_move5()
1433 self.nextthink = self.ltime + 1.0;
1434 self.think = fd_secret_move6;
1435 if (self.noise3 != "")
1436 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1439 void fd_secret_move6()
1441 if (self.noise2 != "")
1442 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1443 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1446 void fd_secret_done()
1448 if (self.spawnflags&SECRET_YES_SHOOT)
1450 self.health = 10000;
1451 self.takedamage = DAMAGE_YES;
1452 //self.th_pain = fd_secret_use;
1454 if (self.noise3 != "")
1455 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1458 void secret_blocked()
1460 if (time < self.attack_finished_single)
1462 self.attack_finished_single = time + 0.5;
1463 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1475 if not(other.iscreature)
1477 if (self.attack_finished_single > time)
1480 self.attack_finished_single = time + 2;
1484 if (other.flags & FL_CLIENT)
1485 centerprint (other, self.message);
1486 play2(other, "misc/talk.wav");
1491 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1492 Basic secret door. Slides back, then to the side. Angle determines direction.
1493 wait = # of seconds before coming back
1494 1st_left = 1st move is left of arrow
1495 1st_down = 1st move is down from arrow
1496 always_shoot = even if targeted, keep shootable
1497 t_width = override WIDTH to move back (or height if going down)
1498 t_length = override LENGTH to move sideways
1499 "dmg" damage to inflict when blocked (2 default)
1501 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1508 void spawnfunc_func_door_secret()
1510 /*if (!self.deathtype) // map makers can override this
1511 self.deathtype = " got in the way";*/
1517 self.mangle = self.angles;
1518 self.angles = '0 0 0';
1519 self.classname = "door";
1520 InitMovingBrushTrigger();
1521 self.effects |= EF_LOWPRECISION;
1523 self.touch = secret_touch;
1524 self.blocked = secret_blocked;
1526 self.use = fd_secret_use;
1531 self.spawnflags |= SECRET_YES_SHOOT;
1533 if(self.spawnflags&SECRET_YES_SHOOT)
1535 self.health = 10000;
1536 self.takedamage = DAMAGE_YES;
1537 self.event_damage = fd_secret_use;
1539 self.oldorigin = self.origin;
1541 self.wait = 5; // 5 seconds before closing