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.mins + '25 25 0';
31 tmax = self.maxs - '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 (other.classname != "player")
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 (other.classname != "player")
98 if (other.health <= 0)
106 void plat_trigger_use()
109 return; // allready 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");
144 .string sound1, sound2;
146 void spawnfunc_path_corner() { };
147 void spawnfunc_func_plat()
154 if (self.sounds == 0)
157 if(self.spawnflags & 4)
160 if(self.dmg && (!self.message))
161 self.message = "was in the wrong place.";
163 if (self.sounds == 1)
165 precache_sound ("plats/plat1.wav");
166 precache_sound ("plats/plat2.wav");
167 self.noise = "plats/plat1.wav";
168 self.noise1 = "plats/plat2.wav";
171 if (self.sounds == 2)
173 precache_sound ("plats/medplat1.wav");
174 precache_sound ("plats/medplat2.wav");
175 self.noise = "plats/medplat1.wav";
176 self.noise1 = "plats/medplat2.wav";
181 precache_sound (self.sound1);
182 self.noise = self.sound1;
186 precache_sound (self.sound2);
187 self.noise1 = self.sound2;
190 self.mangle = self.angles;
191 self.angles = '0 0 0';
193 self.classname = "plat";
194 InitMovingBrushTrigger();
195 self.effects |= EF_LOWPRECISION;
196 setsize (self, self.mins , self.maxs);
198 self.blocked = plat_crush;
203 self.pos1 = self.origin;
204 self.pos2 = self.origin;
205 self.pos2_z = self.origin_z - self.size_z + 8;
207 self.use = plat_trigger_use;
209 plat_spawn_inside_trigger (); // the "start moving" trigger
218 setorigin (self, self.pos2);
226 void() func_train_find;
230 if (time < self.attack_finished_single)
232 self.attack_finished_single = time + 0.5;
236 if (self.think != func_train_find)
245 self.nextthink = self.ltime + self.wait;
246 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
249 self.nextthink = self.ltime + 0.1;
251 self.think = train_next;
258 targ = find (world, targetname, self.target);
259 self.target = targ.target;
261 objerror ("train_next: no next target");
263 self.wait = targ.wait;
266 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
267 SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);
270 void func_train_find()
274 targ = find (world, targetname, self.target);
275 self.target = targ.target;
276 setorigin (self, targ.origin - self.mins);
277 if (!self.targetname)
278 { // not triggered, so start immediately
279 self.nextthink = self.ltime + 0.1;
280 self.think = train_next;
285 void spawnfunc_func_train()
290 objerror ("func_train without a target");
292 if (self.sounds == 0)
294 self.noise = ("misc/null.wav");
295 precache_sound ("misc/null.wav");
296 self.noise1 = ("misc/null.wav");
297 precache_sound ("misc/null.wav");
300 if (self.sounds == 1)
302 self.noise = ("plats/train2.wav");
303 precache_sound ("plats/train2.wav");
304 self.noise1 = ("plats/train1.wav");
305 precache_sound ("plats/train1.wav");
308 self.blocked = train_blocked;
309 self.use = train_use;
310 self.classname = "train";
312 InitMovingBrushTrigger();
313 self.effects |= EF_LOWPRECISION;
314 self.nextthink = self.ltime + 0.1;
315 self.think = func_train_find;
322 self.think = train_next;
323 self.nextthink = self.ltime + self.wait;
329 targ = find(world, targetname, self.target);
330 self.target = targ.target;
332 objerror("train_next: no next target");
333 self.wait = targ.wait;
337 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
339 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
342 void func_train_find()
345 targ = find(world, targetname, self.target);
346 self.target = targ.target;
347 setorigin(self, targ.origin - self.mins);
348 self.nextthink = self.ltime + 1;
349 self.think = train_next;
352 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
353 Ridable platform, targets spawnfunc_path_corner path to follow.
354 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
355 target : targetname of first spawnfunc_path_corner (starts here)
357 void spawnfunc_func_train()
360 objerror("func_train without a target");
364 InitMovingBrushTrigger();
365 self.effects |= EF_LOWPRECISION;
367 // wait for targets to spawn
368 self.nextthink = self.ltime + 0.1;
369 self.think = func_train_find;
374 void rotating_blocked()
377 if(self.dmg && other.takedamage != DAMAGE_NO) {
378 if(self.dmgtime2 < time) {
379 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
380 self.dmgtime2 = time + self.dmgtime;
383 // Gib dead/dying stuff
384 if(other.deadflag != DEAD_NO)
385 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
391 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
392 Brush model that spins in place on one axis (default Z).
393 speed : speed to rotate (in degrees per second)
394 noise : path/name of looping .wav file to play.
395 dmg : Do this mutch dmg every .dmgtime intervall when blocked
399 void spawnfunc_func_rotating()
403 precache_sound(self.noise);
404 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
408 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
409 if (self.spawnflags & 4) // X (untested)
410 self.avelocity = '0 0 1' * self.speed;
411 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
412 else if (self.spawnflags & 8) // Y (untested)
413 self.avelocity = '1 0 0' * self.speed;
414 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
416 self.avelocity = '0 1 0' * self.speed;
418 if(self.dmg & (!self.message))
419 self.message = " was in the wrong place.";
422 if(self.dmg && (!self.dmgtime))
425 self.dmgtime2 = time;
427 InitMovingBrushTrigger();
428 // no EF_LOWPRECISION here, as rounding angles is bad
430 self.blocked = rotating_blocked;
432 // wait for targets to spawn
433 self.nextthink = self.ltime + 999999999;
434 self.think = SUB_Null;
439 void func_bobbing_controller_think()
442 self.nextthink = time + 0.1;
443 // calculate sinewave using makevectors
444 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
445 v = self.owner.destvec + self.owner.movedir * v_forward_y;
446 // * 10 so it will arrive in 0.1 sec
447 self.owner.velocity = (v - self.owner.origin) * 10;
450 void bobbing_blocked()
452 // no need to duplicate code
456 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
457 Brush model that moves back and forth on one axis (default Z).
458 speed : how long one cycle takes in seconds (default 4)
459 height : how far the cycle moves (default 32)
460 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
461 noise : path/name of looping .wav file to play.
462 dmg : Do this mutch dmg every .dmgtime intervall when blocked
465 void spawnfunc_func_bobbing()
467 local entity controller;
470 precache_sound(self.noise);
471 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
477 // center of bobbing motion
478 self.destvec = self.origin;
479 // time scale to get degrees
480 self.cnt = 360 / self.speed;
482 // damage when blocked
483 self.blocked = bobbing_blocked;
484 if(self.dmg & (!self.message))
485 self.message = " was in the wrong place.";
486 if(self.dmg && (!self.dmgtime))
488 self.dmgtime2 = time;
491 if (self.spawnflags & 1) // X
492 self.movedir = '1 0 0' * self.height;
493 else if (self.spawnflags & 2) // Y
494 self.movedir = '0 1 0' * self.height;
496 self.movedir = '0 0 1' * self.height;
498 InitMovingBrushTrigger();
500 // wait for targets to spawn
501 controller = spawn();
502 controller.classname = "func_bobbing_controller";
503 controller.owner = self;
504 controller.nextthink = time + 1;
505 controller.think = func_bobbing_controller_think;
506 self.nextthink = self.ltime + 999999999;
507 self.think = SUB_Null;
509 // Savage: Reduce bandwith, critical on e.g. nexdm02
510 self.effects |= EF_LOWPRECISION;
513 // button and multiple button
516 void() button_return;
520 self.state = STATE_TOP;
521 self.nextthink = self.ltime + self.wait;
522 self.think = button_return;
523 activator = self.enemy;
525 self.frame = 1; // use alternate textures
530 self.state = STATE_BOTTOM;
535 self.state = STATE_DOWN;
536 SUB_CalcMove (self.pos1, self.speed, button_done);
537 self.frame = 0; // use normal textures
539 self.takedamage = DAMAGE_YES; // can be shot again
543 void button_blocked()
545 // do nothing, just don't come all the way back out
551 self.health = self.max_health;
552 self.takedamage = DAMAGE_NO; // will be reset upon return
554 if (self.state == STATE_UP || self.state == STATE_TOP)
557 if (self.noise != "")
558 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
560 self.state = STATE_UP;
561 SUB_CalcMove (self.pos2, self.speed, button_wait);
567 // if (activator.classname != "player")
569 // dprint(activator.classname);
570 // dprint(" triggered a button\n");
572 self.enemy = activator;
578 // if (activator.classname != "player")
580 // dprint(activator.classname);
581 // dprint(" touched a button\n");
585 if (other.classname != "player")
589 self.enemy = other.owner;
593 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
595 self.health = self.health - damage;
596 if (self.health <= 0)
598 // if (activator.classname != "player")
600 // dprint(activator.classname);
601 // dprint(" killed a button\n");
603 self.enemy = damage_attacker;
609 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
610 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.
612 "angle" determines the opening direction
613 "target" all entities with a matching targetname will be used
614 "speed" override the default 40 speed
615 "wait" override the default 1 second wait (-1 = never return)
616 "lip" override the default 4 pixel lip remaining at end of move
617 "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
624 void spawnfunc_func_button()
628 InitMovingBrushTrigger();
629 self.effects |= EF_LOWPRECISION;
631 self.blocked = button_blocked;
632 self.use = button_use;
634 // if (self.health == 0) // all buttons are now shootable
638 self.max_health = self.health;
639 self.event_damage = button_damage;
640 self.takedamage = DAMAGE_YES;
643 self.touch = button_touch;
653 precache_sound(self.noise);
655 self.state = STATE_BOTTOM;
657 self.pos1 = self.origin;
658 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
662 float DOOR_START_OPEN = 1;
663 float DOOR_DONT_LINK = 4;
664 float DOOR_TOGGLE = 32;
668 Doors are similar to buttons, but can spawn a fat trigger field around them
669 to open without a touch, and they link together to form simultanious
672 Door.owner is the master door. If there is only one door, it points to itself.
673 If multiple doors, all will point to a single one.
675 Door.enemy chains from the master door through all doors linked in the chain.
680 =============================================================================
684 =============================================================================
693 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
694 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
697 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
698 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
700 //Dont chamge direction for dead or dying stuff
701 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
704 if (self.state == STATE_DOWN)
710 //gib dying stuff just to make sure
711 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
712 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
716 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
717 // if a door has a negative wait, it would never come back if blocked,
718 // so let it just squash the object to death real fast
719 /* if (self.wait >= 0)
721 if (self.state == STATE_DOWN)
732 if (self.noise1 != "")
733 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
734 self.state = STATE_TOP;
735 if (self.spawnflags & DOOR_TOGGLE)
736 return; // don't come down automatically
737 self.think = door_go_down;
738 self.nextthink = self.ltime + self.wait;
741 void door_hit_bottom()
743 if (self.noise1 != "")
744 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
745 self.state = STATE_BOTTOM;
750 if (self.noise2 != "")
751 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
754 self.takedamage = DAMAGE_YES;
755 self.health = self.max_health;
758 self.state = STATE_DOWN;
759 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
764 if (self.state == STATE_UP)
765 return; // allready going up
767 if (self.state == STATE_TOP)
768 { // reset top wait time
769 self.nextthink = self.ltime + self.wait;
773 if (self.noise2 != "")
774 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
775 self.state = STATE_UP;
776 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
783 =============================================================================
787 =============================================================================
795 if (self.owner != self)
796 objerror ("door_fire: self.owner != self");
798 self.message = ""; // no more message
801 if (self.spawnflags & DOOR_TOGGLE)
803 if (self.state == STATE_UP || self.state == STATE_TOP)
810 } while ( (self != starte) && (self != world) );
816 // trigger all paired doors
822 } while ( (self != starte) && (self != world) );
831 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
832 self.message = ""; // door message are for touch only
834 self.owner.message = "";
836 self.enemy.message = "";
847 void door_trigger_touch()
849 if (other.health < 1)
852 if (time < self.attack_finished_single)
854 self.attack_finished_single = time + 1;
863 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
866 self.health = self.health - damage;
867 if (self.health <= 0)
871 self.health = self.max_health;
872 self.takedamage = DAMAGE_NO; // wil be reset upon return
888 if (other.classname != "player")
890 if (self.owner.attack_finished_single > time)
893 self.owner.attack_finished_single = time + 2;
895 if (self.owner.message != "")
897 if (other.flags & FL_CLIENT)
898 centerprint (other, self.owner.message);
899 play2(other, "misc/talk.wav");
904 =============================================================================
908 =============================================================================
912 entity spawn_field(vector fmins, vector fmaxs)
914 local entity trigger;
918 trigger.classname = "doortriggerfield";
919 trigger.movetype = MOVETYPE_NONE;
920 trigger.solid = SOLID_TRIGGER;
921 trigger.owner = self;
922 trigger.touch = door_trigger_touch;
926 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
931 float EntitiesTouching(entity e1, entity e2)
933 if (e1.mins_x > e2.maxs_x)
935 if (e1.mins_y > e2.maxs_y)
937 if (e1.mins_z > e2.maxs_z)
939 if (e1.maxs_x < e2.mins_x)
941 if (e1.maxs_y < e2.mins_y)
943 if (e1.maxs_z < e2.mins_z)
958 local entity t, starte;
959 local vector cmins, cmaxs;
962 return; // already linked by another door
963 if (self.spawnflags & 4)
965 self.owner = self.enemy = self;
966 return; // don't want to link this door
977 self.owner = starte; // master door
980 starte.health = self.health;
982 starte.targetname = self.targetname;
983 if (self.message != "")
984 starte.message = self.message;
986 t = find(t, classname, self.classname);
989 self.enemy = starte; // make the chain a loop
991 // shootable, or triggered doors just needed the owner/enemy links,
992 // they don't spawn a field
1003 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1008 if (EntitiesTouching(self,t))
1011 objerror ("cross connected doors");
1016 if (t.mins_x < cmins_x)
1018 if (t.mins_y < cmins_y)
1020 if (t.mins_z < cmins_z)
1022 if (t.maxs_x > cmaxs_x)
1024 if (t.maxs_y > cmaxs_y)
1026 if (t.maxs_z > cmaxs_z)
1034 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1035 if two doors touch, they are assumed to be connected and operate as a unit.
1037 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1039 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).
1041 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1042 "angle" determines the opening direction
1043 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1044 "health" if set, door must be shot open
1045 "speed" movement speed (100 default)
1046 "wait" wait before returning (3 default, -1 = never return)
1047 "lip" lip remaining at end of move (8 default)
1048 "dmg" damage to inflict when blocked (2 default)
1055 FIXME: only one sound set available at the time being
1059 void spawnfunc_func_door()
1061 //if (!self.deathtype) // map makers can override this
1062 // self.deathtype = " got in the way";
1065 self.max_health = self.health;
1066 InitMovingBrushTrigger();
1067 self.effects |= EF_LOWPRECISION;
1068 self.classname = "door";
1070 self.blocked = door_blocked;
1071 self.use = door_use;
1073 if(self.targetname == "") {
1074 self.spawnflags = 0;
1078 if(self.spawnflags & 8)
1081 if(self.dmg & (!self.message))
1082 self.message = "was in the wrong place.";
1086 if (self.sounds > 0)
1088 precache_sound ("plats/medplat1.wav");
1089 precache_sound ("plats/medplat2.wav");
1090 self.noise2 = "plats/medplat1.wav";
1091 self.noise1 = "plats/medplat2.wav";
1103 self.pos1 = self.origin;
1104 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1106 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1107 // but spawn in the open position
1108 if (self.spawnflags & DOOR_START_OPEN)
1110 setorigin (self, self.pos2);
1111 self.pos2 = self.pos1;
1112 self.pos1 = self.origin;
1115 self.state = STATE_BOTTOM;
1119 self.takedamage = DAMAGE_YES;
1120 self.event_damage = door_damage;
1126 self.touch = door_touch;
1128 // LinkDoors can't be done until all of the doors have been spawned, so
1129 // the sizes can be detected properly.
1130 self.think = LinkDoors;
1131 self.nextthink = self.ltime + 0.1;
1135 =============================================================================
1139 =============================================================================
1142 void() fd_secret_move1;
1143 void() fd_secret_move2;
1144 void() fd_secret_move3;
1145 void() fd_secret_move4;
1146 void() fd_secret_move5;
1147 void() fd_secret_move6;
1148 void() fd_secret_done;
1150 float SECRET_OPEN_ONCE = 1; // stays open
1151 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1152 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1153 float SECRET_NO_SHOOT = 8; // only opened by trigger
1154 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1157 void fd_secret_use()
1161 self.health = 10000;
1162 self.bot_attack = TRUE;
1164 // exit if still moving around...
1165 if (self.origin != self.oldorigin)
1168 self.message = ""; // no more message
1170 SUB_UseTargets(); // fire all targets / killtargets
1172 self.velocity = '0 0 0';
1174 // Make a sound, wait a little...
1176 if (self.noise1 != "")
1177 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1178 self.nextthink = self.ltime + 0.1;
1180 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1181 makevectors(self.mangle);
1185 if (self.spawnflags & SECRET_1ST_DOWN)
1186 self.t_width = fabs(v_up * self.size);
1188 self.t_width = fabs(v_right * self.size);
1192 self.t_length = fabs(v_forward * self.size);
1194 if (self.spawnflags & SECRET_1ST_DOWN)
1195 self.dest1 = self.origin - v_up * self.t_width;
1197 self.dest1 = self.origin + v_right * (self.t_width * temp);
1199 self.dest2 = self.dest1 + v_forward * self.t_length;
1200 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1201 if (self.noise2 != "")
1202 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1205 // Wait after first movement...
1206 void fd_secret_move1()
1208 self.nextthink = self.ltime + 1.0;
1209 self.think = fd_secret_move2;
1210 if (self.noise3 != "")
1211 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1214 // Start moving sideways w/sound...
1215 void fd_secret_move2()
1217 if (self.noise2 != "")
1218 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1219 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1222 // Wait here until time to go back...
1223 void fd_secret_move3()
1225 if (self.noise3 != "")
1226 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1227 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1229 self.nextthink = self.ltime + self.wait;
1230 self.think = fd_secret_move4;
1235 void fd_secret_move4()
1237 if (self.noise2 != "")
1238 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1239 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1243 void fd_secret_move5()
1245 self.nextthink = self.ltime + 1.0;
1246 self.think = fd_secret_move6;
1247 if (self.noise3 != "")
1248 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1251 void fd_secret_move6()
1253 if (self.noise2 != "")
1254 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1255 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1258 void fd_secret_done()
1260 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1262 self.health = 10000;
1263 self.takedamage = DAMAGE_YES;
1264 //self.th_pain = fd_secret_use;
1266 if (self.noise3 != "")
1267 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1270 void secret_blocked()
1272 if (time < self.attack_finished_single)
1274 self.attack_finished_single = time + 0.5;
1275 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1287 if (activator.classname != "player")
1289 if (self.attack_finished_single > time)
1292 self.attack_finished_single = time + 2;
1296 if (other.flags & FL_CLIENT)
1297 centerprint (other, self.message);
1298 play2(other, "misc/talk.wav");
1303 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1304 Basic secret door. Slides back, then to the side. Angle determines direction.
1305 wait = # of seconds before coming back
1306 1st_left = 1st move is left of arrow
1307 1st_down = 1st move is down from arrow
1308 always_shoot = even if targeted, keep shootable
1309 t_width = override WIDTH to move back (or height if going down)
1310 t_length = override LENGTH to move sideways
1311 "dmg" damage to inflict when blocked (2 default)
1313 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1320 void spawnfunc_func_door_secret()
1322 /*if (!self.deathtype) // map makers can override this
1323 self.deathtype = " got in the way";*/
1329 self.mangle = self.angles;
1330 self.angles = '0 0 0';
1331 self.classname = "door";
1332 InitMovingBrushTrigger();
1333 self.effects |= EF_LOWPRECISION;
1335 self.touch = secret_touch;
1336 self.blocked = secret_blocked;
1338 self.use = fd_secret_use;
1339 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1341 self.health = 10000;
1342 self.takedamage = DAMAGE_YES;
1343 self.event_damage = fd_secret_use;
1345 self.oldorigin = self.origin;
1347 self.wait = 5; // 5 seconds before closing