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 =============================================================================
796 if (self.owner != self)
797 objerror ("door_fire: self.owner != self");
799 oldmessage = self.message;
800 self.message = ""; // no more message
803 if (self.spawnflags & DOOR_TOGGLE)
805 if (self.state == STATE_UP || self.state == STATE_TOP)
812 } while ( (self != starte) && (self != world) );
818 // trigger all paired doors
824 } while ( (self != starte) && (self != world) );
827 self.message = oldmessage;
835 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
846 void door_trigger_touch()
848 if (other.health < 1)
851 if (time < self.attack_finished_single)
853 self.attack_finished_single = time + 1;
862 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
865 self.health = self.health - damage;
866 if (self.health <= 0)
870 self.health = self.max_health;
871 self.takedamage = DAMAGE_NO; // wil be reset upon return
887 if (other.classname != "player")
889 if (self.owner.attack_finished_single > time)
892 self.owner.attack_finished_single = time + 2;
894 if (self.owner.message != "")
896 if (other.flags & FL_CLIENT)
897 centerprint (other, self.owner.message);
898 play2(other, "misc/talk.wav");
903 =============================================================================
907 =============================================================================
911 entity spawn_field(vector fmins, vector fmaxs)
913 local entity trigger;
917 trigger.classname = "doortriggerfield";
918 trigger.movetype = MOVETYPE_NONE;
919 trigger.solid = SOLID_TRIGGER;
920 trigger.owner = self;
921 trigger.touch = door_trigger_touch;
925 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
930 float EntitiesTouching(entity e1, entity e2)
932 if (e1.mins_x > e2.maxs_x)
934 if (e1.mins_y > e2.maxs_y)
936 if (e1.mins_z > e2.maxs_z)
938 if (e1.maxs_x < e2.mins_x)
940 if (e1.maxs_y < e2.mins_y)
942 if (e1.maxs_z < e2.mins_z)
957 local entity t, starte;
958 local vector cmins, cmaxs;
961 return; // already linked by another door
962 if (self.spawnflags & 4)
964 self.owner = self.enemy = self;
965 return; // don't want to link this door
976 self.owner = starte; // master door
979 starte.health = self.health;
981 starte.targetname = self.targetname;
982 if (self.message != "")
983 starte.message = self.message;
985 t = find(t, classname, self.classname);
988 self.enemy = starte; // make the chain a loop
990 // shootable, or triggered doors just needed the owner/enemy links,
991 // they don't spawn a field
1002 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1007 if (EntitiesTouching(self,t))
1010 objerror ("cross connected doors");
1015 if (t.mins_x < cmins_x)
1017 if (t.mins_y < cmins_y)
1019 if (t.mins_z < cmins_z)
1021 if (t.maxs_x > cmaxs_x)
1023 if (t.maxs_y > cmaxs_y)
1025 if (t.maxs_z > cmaxs_z)
1033 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1034 if two doors touch, they are assumed to be connected and operate as a unit.
1036 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1038 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).
1040 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1041 "angle" determines the opening direction
1042 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1043 "health" if set, door must be shot open
1044 "speed" movement speed (100 default)
1045 "wait" wait before returning (3 default, -1 = never return)
1046 "lip" lip remaining at end of move (8 default)
1047 "dmg" damage to inflict when blocked (2 default)
1054 FIXME: only one sound set available at the time being
1058 void spawnfunc_func_door()
1060 //if (!self.deathtype) // map makers can override this
1061 // self.deathtype = " got in the way";
1064 self.max_health = self.health;
1065 InitMovingBrushTrigger();
1066 self.effects |= EF_LOWPRECISION;
1067 self.classname = "door";
1069 self.blocked = door_blocked;
1070 self.use = door_use;
1072 if(self.targetname == "") {
1073 self.spawnflags = 0;
1077 if(self.spawnflags & 8)
1080 if(self.dmg & (!self.message))
1081 self.message = "was in the wrong place.";
1085 if (self.sounds > 0)
1087 precache_sound ("plats/medplat1.wav");
1088 precache_sound ("plats/medplat2.wav");
1089 self.noise2 = "plats/medplat1.wav";
1090 self.noise1 = "plats/medplat2.wav";
1102 self.pos1 = self.origin;
1103 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1105 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1106 // but spawn in the open position
1107 if (self.spawnflags & DOOR_START_OPEN)
1109 setorigin (self, self.pos2);
1110 self.pos2 = self.pos1;
1111 self.pos1 = self.origin;
1114 self.state = STATE_BOTTOM;
1118 self.takedamage = DAMAGE_YES;
1119 self.event_damage = door_damage;
1125 self.touch = door_touch;
1127 // LinkDoors can't be done until all of the doors have been spawned, so
1128 // the sizes can be detected properly.
1129 self.think = LinkDoors;
1130 self.nextthink = self.ltime + 0.1;
1134 =============================================================================
1138 =============================================================================
1141 void() fd_secret_move1;
1142 void() fd_secret_move2;
1143 void() fd_secret_move3;
1144 void() fd_secret_move4;
1145 void() fd_secret_move5;
1146 void() fd_secret_move6;
1147 void() fd_secret_done;
1149 float SECRET_OPEN_ONCE = 1; // stays open
1150 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1151 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1152 float SECRET_NO_SHOOT = 8; // only opened by trigger
1153 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1156 void fd_secret_use()
1159 string message_save;
1161 self.health = 10000;
1162 self.bot_attack = TRUE;
1164 // exit if still moving around...
1165 if (self.origin != self.oldorigin)
1168 message_save = self.message;
1169 self.message = ""; // no more message
1170 SUB_UseTargets(); // fire all targets / killtargets
1171 self.message = message_save;
1173 self.velocity = '0 0 0';
1175 // Make a sound, wait a little...
1177 if (self.noise1 != "")
1178 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1179 self.nextthink = self.ltime + 0.1;
1181 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1182 makevectors(self.mangle);
1186 if (self.spawnflags & SECRET_1ST_DOWN)
1187 self.t_width = fabs(v_up * self.size);
1189 self.t_width = fabs(v_right * self.size);
1193 self.t_length = fabs(v_forward * self.size);
1195 if (self.spawnflags & SECRET_1ST_DOWN)
1196 self.dest1 = self.origin - v_up * self.t_width;
1198 self.dest1 = self.origin + v_right * (self.t_width * temp);
1200 self.dest2 = self.dest1 + v_forward * self.t_length;
1201 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1202 if (self.noise2 != "")
1203 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1206 // Wait after first movement...
1207 void fd_secret_move1()
1209 self.nextthink = self.ltime + 1.0;
1210 self.think = fd_secret_move2;
1211 if (self.noise3 != "")
1212 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1215 // Start moving sideways w/sound...
1216 void fd_secret_move2()
1218 if (self.noise2 != "")
1219 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1220 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1223 // Wait here until time to go back...
1224 void fd_secret_move3()
1226 if (self.noise3 != "")
1227 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1228 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1230 self.nextthink = self.ltime + self.wait;
1231 self.think = fd_secret_move4;
1236 void fd_secret_move4()
1238 if (self.noise2 != "")
1239 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1240 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1244 void fd_secret_move5()
1246 self.nextthink = self.ltime + 1.0;
1247 self.think = fd_secret_move6;
1248 if (self.noise3 != "")
1249 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1252 void fd_secret_move6()
1254 if (self.noise2 != "")
1255 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1256 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1259 void fd_secret_done()
1261 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1263 self.health = 10000;
1264 self.takedamage = DAMAGE_YES;
1265 //self.th_pain = fd_secret_use;
1267 if (self.noise3 != "")
1268 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1271 void secret_blocked()
1273 if (time < self.attack_finished_single)
1275 self.attack_finished_single = time + 0.5;
1276 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1288 if (activator.classname != "player")
1290 if (self.attack_finished_single > time)
1293 self.attack_finished_single = time + 2;
1297 if (other.flags & FL_CLIENT)
1298 centerprint (other, self.message);
1299 play2(other, "misc/talk.wav");
1304 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1305 Basic secret door. Slides back, then to the side. Angle determines direction.
1306 wait = # of seconds before coming back
1307 1st_left = 1st move is left of arrow
1308 1st_down = 1st move is down from arrow
1309 always_shoot = even if targeted, keep shootable
1310 t_width = override WIDTH to move back (or height if going down)
1311 t_length = override LENGTH to move sideways
1312 "dmg" damage to inflict when blocked (2 default)
1314 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1321 void spawnfunc_func_door_secret()
1323 /*if (!self.deathtype) // map makers can override this
1324 self.deathtype = " got in the way";*/
1330 self.mangle = self.angles;
1331 self.angles = '0 0 0';
1332 self.classname = "door";
1333 InitMovingBrushTrigger();
1334 self.effects |= EF_LOWPRECISION;
1336 self.touch = secret_touch;
1337 self.blocked = secret_blocked;
1339 self.use = fd_secret_use;
1340 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1342 self.health = 10000;
1343 self.takedamage = DAMAGE_YES;
1344 self.event_damage = fd_secret_use;
1346 self.oldorigin = self.origin;
1348 self.wait = 5; // 5 seconds before closing