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");
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 in the wrong place.";
168 if (self.sounds == 1)
170 precache_sound ("plats/plat1.wav");
171 precache_sound ("plats/plat2.wav");
172 self.noise = "plats/plat1.wav";
173 self.noise1 = "plats/plat2.wav";
176 if (self.sounds == 2)
178 precache_sound ("plats/medplat1.wav");
179 precache_sound ("plats/medplat2.wav");
180 self.noise = "plats/medplat1.wav";
181 self.noise1 = "plats/medplat2.wav";
186 precache_sound (self.sound1);
187 self.noise = self.sound1;
191 precache_sound (self.sound2);
192 self.noise1 = self.sound2;
195 self.mangle = self.angles;
196 self.angles = '0 0 0';
198 self.classname = "plat";
199 InitMovingBrushTrigger();
200 self.effects |= EF_LOWPRECISION;
201 setsize (self, self.mins , self.maxs);
203 self.blocked = plat_crush;
208 self.pos1 = self.origin;
209 self.pos2 = self.origin;
210 self.pos2_z = self.origin_z - self.size_z + 8;
212 self.use = plat_trigger_use;
214 plat_spawn_inside_trigger (); // the "start moving" trigger
222 InitializeEntity(self, plat_init_movedown, INITPRIO_SETLOCATION);
229 self.think = train_next;
230 self.nextthink = self.ltime + self.wait;
236 targ = find(world, targetname, self.target);
237 self.target = targ.target;
239 objerror("train_next: no next target");
240 self.wait = targ.wait;
246 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
248 SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
253 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
255 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
259 void func_train_find()
262 targ = find(world, targetname, self.target);
263 self.target = targ.target;
265 objerror("func_train_find: no next target");
266 setorigin(self, targ.origin - self.mins);
267 self.nextthink = self.ltime + 1;
268 self.think = train_next;
271 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
272 Ridable platform, targets spawnfunc_path_corner path to follow.
273 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
274 target : targetname of first spawnfunc_path_corner (starts here)
276 void spawnfunc_func_train()
279 objerror("func_train without a target");
283 InitMovingBrushTrigger();
284 self.effects |= EF_LOWPRECISION;
286 // wait for targets to spawn
287 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
292 void rotating_blocked()
295 if(self.dmg && other.takedamage != DAMAGE_NO) {
296 if(self.dmgtime2 < time) {
297 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
298 self.dmgtime2 = time + self.dmgtime;
301 // Gib dead/dying stuff
302 if(other.deadflag != DEAD_NO)
303 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
309 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
310 Brush model that spins in place on one axis (default Z).
311 speed : speed to rotate (in degrees per second)
312 noise : path/name of looping .wav file to play.
313 dmg : Do this mutch dmg every .dmgtime intervall when blocked
317 void spawnfunc_func_rotating()
321 precache_sound(self.noise);
322 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
326 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
327 if (self.spawnflags & 4) // X (untested)
328 self.avelocity = '0 0 1' * self.speed;
329 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
330 else if (self.spawnflags & 8) // Y (untested)
331 self.avelocity = '1 0 0' * self.speed;
332 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
334 self.avelocity = '0 1 0' * self.speed;
336 if(self.dmg & (!self.message))
337 self.message = " was in the wrong place.";
340 if(self.dmg && (!self.dmgtime))
343 self.dmgtime2 = time;
345 InitMovingBrushTrigger();
346 // no EF_LOWPRECISION here, as rounding angles is bad
348 self.blocked = rotating_blocked;
350 // wait for targets to spawn
351 self.nextthink = self.ltime + 999999999;
352 self.think = SUB_Null;
356 void func_bobbing_controller_think()
359 self.nextthink = time + 0.1;
360 // calculate sinewave using makevectors
361 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
362 v = self.owner.destvec + self.owner.movedir * v_forward_y;
363 // * 10 so it will arrive in 0.1 sec
364 self.owner.velocity = (v - self.owner.origin) * 10;
367 void bobbing_blocked()
369 // no need to duplicate code
373 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
374 Brush model that moves back and forth on one axis (default Z).
375 speed : how long one cycle takes in seconds (default 4)
376 height : how far the cycle moves (default 32)
377 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
378 noise : path/name of looping .wav file to play.
379 dmg : Do this mutch dmg every .dmgtime intervall when blocked
382 void spawnfunc_func_bobbing()
384 local entity controller;
387 precache_sound(self.noise);
388 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
394 // center of bobbing motion
395 self.destvec = self.origin;
396 // time scale to get degrees
397 self.cnt = 360 / self.speed;
399 // damage when blocked
400 self.blocked = bobbing_blocked;
401 if(self.dmg & (!self.message))
402 self.message = " was in the wrong place.";
403 if(self.dmg && (!self.dmgtime))
405 self.dmgtime2 = time;
408 if (self.spawnflags & 1) // X
409 self.movedir = '1 0 0' * self.height;
410 else if (self.spawnflags & 2) // Y
411 self.movedir = '0 1 0' * self.height;
413 self.movedir = '0 0 1' * self.height;
415 InitMovingBrushTrigger();
417 // wait for targets to spawn
418 controller = spawn();
419 controller.classname = "func_bobbing_controller";
420 controller.owner = self;
421 controller.nextthink = time + 1;
422 controller.think = func_bobbing_controller_think;
423 self.nextthink = self.ltime + 999999999;
424 self.think = SUB_Null;
426 // Savage: Reduce bandwith, critical on e.g. nexdm02
427 self.effects |= EF_LOWPRECISION;
430 // button and multiple button
433 void() button_return;
437 self.state = STATE_TOP;
438 self.nextthink = self.ltime + self.wait;
439 self.think = button_return;
440 activator = self.enemy;
442 self.frame = 1; // use alternate textures
447 self.state = STATE_BOTTOM;
452 self.state = STATE_DOWN;
453 SUB_CalcMove (self.pos1, self.speed, button_done);
454 self.frame = 0; // use normal textures
456 self.takedamage = DAMAGE_YES; // can be shot again
460 void button_blocked()
462 // do nothing, just don't come all the way back out
468 self.health = self.max_health;
469 self.takedamage = DAMAGE_NO; // will be reset upon return
471 if (self.state == STATE_UP || self.state == STATE_TOP)
474 if (self.noise != "")
475 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
477 self.state = STATE_UP;
478 SUB_CalcMove (self.pos2, self.speed, button_wait);
484 // if (activator.classname != "player")
486 // dprint(activator.classname);
487 // dprint(" triggered a button\n");
489 self.enemy = activator;
495 // if (activator.classname != "player")
497 // dprint(activator.classname);
498 // dprint(" touched a button\n");
502 if (other.classname != "player")
506 self.enemy = other.owner;
510 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
512 self.health = self.health - damage;
513 if (self.health <= 0)
515 // if (activator.classname != "player")
517 // dprint(activator.classname);
518 // dprint(" killed a button\n");
520 self.enemy = damage_attacker;
526 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
527 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.
529 "angle" determines the opening direction
530 "target" all entities with a matching targetname will be used
531 "speed" override the default 40 speed
532 "wait" override the default 1 second wait (-1 = never return)
533 "lip" override the default 4 pixel lip remaining at end of move
534 "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
541 void spawnfunc_func_button()
545 InitMovingBrushTrigger();
546 self.effects |= EF_LOWPRECISION;
548 self.blocked = button_blocked;
549 self.use = button_use;
551 // if (self.health == 0) // all buttons are now shootable
555 self.max_health = self.health;
556 self.event_damage = button_damage;
557 self.takedamage = DAMAGE_YES;
560 self.touch = button_touch;
570 precache_sound(self.noise);
572 self.state = STATE_BOTTOM;
574 self.pos1 = self.origin;
575 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
579 float DOOR_START_OPEN = 1;
580 float DOOR_DONT_LINK = 4;
581 float DOOR_TOGGLE = 32;
585 Doors are similar to buttons, but can spawn a fat trigger field around them
586 to open without a touch, and they link together to form simultanious
589 Door.owner is the master door. If there is only one door, it points to itself.
590 If multiple doors, all will point to a single one.
592 Door.enemy chains from the master door through all doors linked in the chain.
597 =============================================================================
601 =============================================================================
610 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
611 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
614 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
615 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
617 //Dont chamge direction for dead or dying stuff
618 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
621 if (self.state == STATE_DOWN)
627 //gib dying stuff just to make sure
628 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
629 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
633 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
634 // if a door has a negative wait, it would never come back if blocked,
635 // so let it just squash the object to death real fast
636 /* if (self.wait >= 0)
638 if (self.state == STATE_DOWN)
649 if (self.noise1 != "")
650 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
651 self.state = STATE_TOP;
652 if (self.spawnflags & DOOR_TOGGLE)
653 return; // don't come down automatically
654 self.think = door_go_down;
655 self.nextthink = self.ltime + self.wait;
658 void door_hit_bottom()
660 if (self.noise1 != "")
661 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
662 self.state = STATE_BOTTOM;
667 if (self.noise2 != "")
668 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
671 self.takedamage = DAMAGE_YES;
672 self.health = self.max_health;
675 self.state = STATE_DOWN;
676 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
681 if (self.state == STATE_UP)
682 return; // allready going up
684 if (self.state == STATE_TOP)
685 { // reset top wait time
686 self.nextthink = self.ltime + self.wait;
690 if (self.noise2 != "")
691 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
692 self.state = STATE_UP;
693 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
700 =============================================================================
704 =============================================================================
713 if (self.owner != self)
714 objerror ("door_fire: self.owner != self");
716 oldmessage = self.message;
717 self.message = ""; // no more message
720 if (self.spawnflags & DOOR_TOGGLE)
722 if (self.state == STATE_UP || self.state == STATE_TOP)
729 } while ( (self != starte) && (self != world) );
735 // trigger all paired doors
741 } while ( (self != starte) && (self != world) );
744 self.message = oldmessage;
752 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
763 void door_trigger_touch()
765 if (other.health < 1)
768 if (time < self.attack_finished_single)
770 self.attack_finished_single = time + 1;
779 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
782 self.health = self.health - damage;
783 if (self.health <= 0)
787 self.health = self.max_health;
788 self.takedamage = DAMAGE_NO; // wil be reset upon return
804 if (other.classname != "player")
806 if (self.owner.attack_finished_single > time)
809 self.owner.attack_finished_single = time + 2;
811 if (self.owner.message != "")
813 if (other.flags & FL_CLIENT)
814 centerprint (other, self.owner.message);
815 play2(other, "misc/talk.wav");
820 =============================================================================
824 =============================================================================
828 entity spawn_field(vector fmins, vector fmaxs)
830 local entity trigger;
834 trigger.classname = "doortriggerfield";
835 trigger.movetype = MOVETYPE_NONE;
836 trigger.solid = SOLID_TRIGGER;
837 trigger.owner = self;
838 trigger.touch = door_trigger_touch;
842 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
847 float EntitiesTouching(entity e1, entity e2)
849 if (e1.mins_x > e2.maxs_x)
851 if (e1.mins_y > e2.maxs_y)
853 if (e1.mins_z > e2.maxs_z)
855 if (e1.maxs_x < e2.mins_x)
857 if (e1.maxs_y < e2.mins_y)
859 if (e1.maxs_z < e2.mins_z)
874 local entity t, starte;
875 local vector cmins, cmaxs;
878 return; // already linked by another door
879 if (self.spawnflags & 4)
881 self.owner = self.enemy = self;
882 return; // don't want to link this door
893 self.owner = starte; // master door
896 starte.health = self.health;
898 starte.targetname = self.targetname;
899 if (self.message != "")
900 starte.message = self.message;
902 t = find(t, classname, self.classname);
905 self.enemy = starte; // make the chain a loop
907 // shootable, or triggered doors just needed the owner/enemy links,
908 // they don't spawn a field
919 self.owner.trigger_field = spawn_field(cmins, cmaxs);
924 if (EntitiesTouching(self,t))
927 objerror ("cross connected doors");
932 if (t.mins_x < cmins_x)
934 if (t.mins_y < cmins_y)
936 if (t.mins_z < cmins_z)
938 if (t.maxs_x > cmaxs_x)
940 if (t.maxs_y > cmaxs_y)
942 if (t.maxs_z > cmaxs_z)
950 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
951 if two doors touch, they are assumed to be connected and operate as a unit.
953 TOGGLE causes the door to wait in both the start and end states for a trigger event.
955 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).
957 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
958 "angle" determines the opening direction
959 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
960 "health" if set, door must be shot open
961 "speed" movement speed (100 default)
962 "wait" wait before returning (3 default, -1 = never return)
963 "lip" lip remaining at end of move (8 default)
964 "dmg" damage to inflict when blocked (2 default)
971 FIXME: only one sound set available at the time being
975 void door_init_startopen()
977 setorigin (self, self.pos2);
978 self.pos2 = self.pos1;
979 self.pos1 = self.origin;
982 void spawnfunc_func_door()
984 //if (!self.deathtype) // map makers can override this
985 // self.deathtype = " got in the way";
988 self.max_health = self.health;
989 InitMovingBrushTrigger();
990 self.effects |= EF_LOWPRECISION;
991 self.classname = "door";
993 self.blocked = door_blocked;
996 if(self.spawnflags & 8)
999 if(self.dmg & (!self.message))
1000 self.message = "was in the wrong place.";
1004 if (self.sounds > 0)
1006 precache_sound ("plats/medplat1.wav");
1007 precache_sound ("plats/medplat2.wav");
1008 self.noise2 = "plats/medplat1.wav";
1009 self.noise1 = "plats/medplat2.wav";
1021 self.pos1 = self.origin;
1022 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1024 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1025 // but spawn in the open position
1026 if (self.spawnflags & DOOR_START_OPEN)
1027 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1029 self.state = STATE_BOTTOM;
1033 self.takedamage = DAMAGE_YES;
1034 self.event_damage = door_damage;
1040 self.touch = door_touch;
1042 // LinkDoors can't be done until all of the doors have been spawned, so
1043 // the sizes can be detected properly.
1044 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1048 =============================================================================
1052 =============================================================================
1055 void() fd_secret_move1;
1056 void() fd_secret_move2;
1057 void() fd_secret_move3;
1058 void() fd_secret_move4;
1059 void() fd_secret_move5;
1060 void() fd_secret_move6;
1061 void() fd_secret_done;
1063 float SECRET_OPEN_ONCE = 1; // stays open
1064 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1065 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1066 float SECRET_NO_SHOOT = 8; // only opened by trigger
1067 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1070 void fd_secret_use()
1073 string message_save;
1075 self.health = 10000;
1076 self.bot_attack = TRUE;
1078 // exit if still moving around...
1079 if (self.origin != self.oldorigin)
1082 message_save = self.message;
1083 self.message = ""; // no more message
1084 SUB_UseTargets(); // fire all targets / killtargets
1085 self.message = message_save;
1087 self.velocity = '0 0 0';
1089 // Make a sound, wait a little...
1091 if (self.noise1 != "")
1092 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1093 self.nextthink = self.ltime + 0.1;
1095 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1096 makevectors(self.mangle);
1100 if (self.spawnflags & SECRET_1ST_DOWN)
1101 self.t_width = fabs(v_up * self.size);
1103 self.t_width = fabs(v_right * self.size);
1107 self.t_length = fabs(v_forward * self.size);
1109 if (self.spawnflags & SECRET_1ST_DOWN)
1110 self.dest1 = self.origin - v_up * self.t_width;
1112 self.dest1 = self.origin + v_right * (self.t_width * temp);
1114 self.dest2 = self.dest1 + v_forward * self.t_length;
1115 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1116 if (self.noise2 != "")
1117 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1120 // Wait after first movement...
1121 void fd_secret_move1()
1123 self.nextthink = self.ltime + 1.0;
1124 self.think = fd_secret_move2;
1125 if (self.noise3 != "")
1126 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1129 // Start moving sideways w/sound...
1130 void fd_secret_move2()
1132 if (self.noise2 != "")
1133 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1134 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1137 // Wait here until time to go back...
1138 void fd_secret_move3()
1140 if (self.noise3 != "")
1141 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1142 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1144 self.nextthink = self.ltime + self.wait;
1145 self.think = fd_secret_move4;
1150 void fd_secret_move4()
1152 if (self.noise2 != "")
1153 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1154 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1158 void fd_secret_move5()
1160 self.nextthink = self.ltime + 1.0;
1161 self.think = fd_secret_move6;
1162 if (self.noise3 != "")
1163 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1166 void fd_secret_move6()
1168 if (self.noise2 != "")
1169 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1170 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1173 void fd_secret_done()
1175 if (self.spawnflags&SECRET_YES_SHOOT)
1177 self.health = 10000;
1178 self.takedamage = DAMAGE_YES;
1179 //self.th_pain = fd_secret_use;
1181 if (self.noise3 != "")
1182 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1185 void secret_blocked()
1187 if (time < self.attack_finished_single)
1189 self.attack_finished_single = time + 0.5;
1190 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1202 if (activator.classname != "player")
1204 if (self.attack_finished_single > time)
1207 self.attack_finished_single = time + 2;
1211 if (other.flags & FL_CLIENT)
1212 centerprint (other, self.message);
1213 play2(other, "misc/talk.wav");
1218 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1219 Basic secret door. Slides back, then to the side. Angle determines direction.
1220 wait = # of seconds before coming back
1221 1st_left = 1st move is left of arrow
1222 1st_down = 1st move is down from arrow
1223 always_shoot = even if targeted, keep shootable
1224 t_width = override WIDTH to move back (or height if going down)
1225 t_length = override LENGTH to move sideways
1226 "dmg" damage to inflict when blocked (2 default)
1228 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1235 void spawnfunc_func_door_secret()
1237 /*if (!self.deathtype) // map makers can override this
1238 self.deathtype = " got in the way";*/
1244 self.mangle = self.angles;
1245 self.angles = '0 0 0';
1246 self.classname = "door";
1247 InitMovingBrushTrigger();
1248 self.effects |= EF_LOWPRECISION;
1250 self.touch = secret_touch;
1251 self.blocked = secret_blocked;
1253 self.use = fd_secret_use;
1255 self.spawnflags |= SECRET_YES_SHOOT;
1257 if(self.spawnflags&SECRET_YES_SHOOT)
1259 self.health = 10000;
1260 self.takedamage = DAMAGE_YES;
1261 self.event_damage = fd_secret_use;
1263 self.oldorigin = self.origin;
1265 self.wait = 5; // 5 seconds before closing