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;
348 objerror("func_train_find: no next target");
349 setorigin(self, targ.origin - self.mins);
350 self.nextthink = self.ltime + 1;
351 self.think = train_next;
354 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
355 Ridable platform, targets spawnfunc_path_corner path to follow.
356 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
357 target : targetname of first spawnfunc_path_corner (starts here)
359 void spawnfunc_func_train()
362 objerror("func_train without a target");
366 InitMovingBrushTrigger();
367 self.effects |= EF_LOWPRECISION;
369 // wait for targets to spawn
370 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
375 void rotating_blocked()
378 if(self.dmg && other.takedamage != DAMAGE_NO) {
379 if(self.dmgtime2 < time) {
380 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
381 self.dmgtime2 = time + self.dmgtime;
384 // Gib dead/dying stuff
385 if(other.deadflag != DEAD_NO)
386 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
392 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
393 Brush model that spins in place on one axis (default Z).
394 speed : speed to rotate (in degrees per second)
395 noise : path/name of looping .wav file to play.
396 dmg : Do this mutch dmg every .dmgtime intervall when blocked
400 void spawnfunc_func_rotating()
404 precache_sound(self.noise);
405 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
409 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
410 if (self.spawnflags & 4) // X (untested)
411 self.avelocity = '0 0 1' * self.speed;
412 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
413 else if (self.spawnflags & 8) // Y (untested)
414 self.avelocity = '1 0 0' * self.speed;
415 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
417 self.avelocity = '0 1 0' * self.speed;
419 if(self.dmg & (!self.message))
420 self.message = " was in the wrong place.";
423 if(self.dmg && (!self.dmgtime))
426 self.dmgtime2 = time;
428 InitMovingBrushTrigger();
429 // no EF_LOWPRECISION here, as rounding angles is bad
431 self.blocked = rotating_blocked;
433 // wait for targets to spawn
434 self.nextthink = self.ltime + 999999999;
435 self.think = SUB_Null;
440 void func_bobbing_controller_think()
443 self.nextthink = time + 0.1;
444 // calculate sinewave using makevectors
445 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
446 v = self.owner.destvec + self.owner.movedir * v_forward_y;
447 // * 10 so it will arrive in 0.1 sec
448 self.owner.velocity = (v - self.owner.origin) * 10;
451 void bobbing_blocked()
453 // no need to duplicate code
457 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
458 Brush model that moves back and forth on one axis (default Z).
459 speed : how long one cycle takes in seconds (default 4)
460 height : how far the cycle moves (default 32)
461 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
462 noise : path/name of looping .wav file to play.
463 dmg : Do this mutch dmg every .dmgtime intervall when blocked
466 void spawnfunc_func_bobbing()
468 local entity controller;
471 precache_sound(self.noise);
472 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
478 // center of bobbing motion
479 self.destvec = self.origin;
480 // time scale to get degrees
481 self.cnt = 360 / self.speed;
483 // damage when blocked
484 self.blocked = bobbing_blocked;
485 if(self.dmg & (!self.message))
486 self.message = " was in the wrong place.";
487 if(self.dmg && (!self.dmgtime))
489 self.dmgtime2 = time;
492 if (self.spawnflags & 1) // X
493 self.movedir = '1 0 0' * self.height;
494 else if (self.spawnflags & 2) // Y
495 self.movedir = '0 1 0' * self.height;
497 self.movedir = '0 0 1' * self.height;
499 InitMovingBrushTrigger();
501 // wait for targets to spawn
502 controller = spawn();
503 controller.classname = "func_bobbing_controller";
504 controller.owner = self;
505 controller.nextthink = time + 1;
506 controller.think = func_bobbing_controller_think;
507 self.nextthink = self.ltime + 999999999;
508 self.think = SUB_Null;
510 // Savage: Reduce bandwith, critical on e.g. nexdm02
511 self.effects |= EF_LOWPRECISION;
514 // button and multiple button
517 void() button_return;
521 self.state = STATE_TOP;
522 self.nextthink = self.ltime + self.wait;
523 self.think = button_return;
524 activator = self.enemy;
526 self.frame = 1; // use alternate textures
531 self.state = STATE_BOTTOM;
536 self.state = STATE_DOWN;
537 SUB_CalcMove (self.pos1, self.speed, button_done);
538 self.frame = 0; // use normal textures
540 self.takedamage = DAMAGE_YES; // can be shot again
544 void button_blocked()
546 // do nothing, just don't come all the way back out
552 self.health = self.max_health;
553 self.takedamage = DAMAGE_NO; // will be reset upon return
555 if (self.state == STATE_UP || self.state == STATE_TOP)
558 if (self.noise != "")
559 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
561 self.state = STATE_UP;
562 SUB_CalcMove (self.pos2, self.speed, button_wait);
568 // if (activator.classname != "player")
570 // dprint(activator.classname);
571 // dprint(" triggered a button\n");
573 self.enemy = activator;
579 // if (activator.classname != "player")
581 // dprint(activator.classname);
582 // dprint(" touched a button\n");
586 if (other.classname != "player")
590 self.enemy = other.owner;
594 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
596 self.health = self.health - damage;
597 if (self.health <= 0)
599 // if (activator.classname != "player")
601 // dprint(activator.classname);
602 // dprint(" killed a button\n");
604 self.enemy = damage_attacker;
610 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
611 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.
613 "angle" determines the opening direction
614 "target" all entities with a matching targetname will be used
615 "speed" override the default 40 speed
616 "wait" override the default 1 second wait (-1 = never return)
617 "lip" override the default 4 pixel lip remaining at end of move
618 "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
625 void spawnfunc_func_button()
629 InitMovingBrushTrigger();
630 self.effects |= EF_LOWPRECISION;
632 self.blocked = button_blocked;
633 self.use = button_use;
635 // if (self.health == 0) // all buttons are now shootable
639 self.max_health = self.health;
640 self.event_damage = button_damage;
641 self.takedamage = DAMAGE_YES;
644 self.touch = button_touch;
654 precache_sound(self.noise);
656 self.state = STATE_BOTTOM;
658 self.pos1 = self.origin;
659 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
663 float DOOR_START_OPEN = 1;
664 float DOOR_DONT_LINK = 4;
665 float DOOR_TOGGLE = 32;
669 Doors are similar to buttons, but can spawn a fat trigger field around them
670 to open without a touch, and they link together to form simultanious
673 Door.owner is the master door. If there is only one door, it points to itself.
674 If multiple doors, all will point to a single one.
676 Door.enemy chains from the master door through all doors linked in the chain.
681 =============================================================================
685 =============================================================================
694 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
695 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
698 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
699 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
701 //Dont chamge direction for dead or dying stuff
702 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
705 if (self.state == STATE_DOWN)
711 //gib dying stuff just to make sure
712 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
713 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
717 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
718 // if a door has a negative wait, it would never come back if blocked,
719 // so let it just squash the object to death real fast
720 /* if (self.wait >= 0)
722 if (self.state == STATE_DOWN)
733 if (self.noise1 != "")
734 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
735 self.state = STATE_TOP;
736 if (self.spawnflags & DOOR_TOGGLE)
737 return; // don't come down automatically
738 self.think = door_go_down;
739 self.nextthink = self.ltime + self.wait;
742 void door_hit_bottom()
744 if (self.noise1 != "")
745 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
746 self.state = STATE_BOTTOM;
751 if (self.noise2 != "")
752 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
755 self.takedamage = DAMAGE_YES;
756 self.health = self.max_health;
759 self.state = STATE_DOWN;
760 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
765 if (self.state == STATE_UP)
766 return; // allready going up
768 if (self.state == STATE_TOP)
769 { // reset top wait time
770 self.nextthink = self.ltime + self.wait;
774 if (self.noise2 != "")
775 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
776 self.state = STATE_UP;
777 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
784 =============================================================================
788 =============================================================================
797 if (self.owner != self)
798 objerror ("door_fire: self.owner != self");
800 oldmessage = self.message;
801 self.message = ""; // no more message
804 if (self.spawnflags & DOOR_TOGGLE)
806 if (self.state == STATE_UP || self.state == STATE_TOP)
813 } while ( (self != starte) && (self != world) );
819 // trigger all paired doors
825 } while ( (self != starte) && (self != world) );
828 self.message = oldmessage;
836 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
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 InitializeEntity(self, LinkDoors, INITPRIO_FINDTARGET);
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