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);
227 self.think = train_next;
228 self.nextthink = self.ltime + self.wait;
234 targ = find(world, targetname, self.target);
235 self.target = targ.target;
237 objerror("train_next: no next target");
238 self.wait = targ.wait;
242 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
244 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
247 void func_train_find()
250 targ = find(world, targetname, self.target);
251 self.target = targ.target;
253 objerror("func_train_find: no next target");
254 setorigin(self, targ.origin - self.mins);
255 self.nextthink = self.ltime + 1;
256 self.think = train_next;
259 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
260 Ridable platform, targets spawnfunc_path_corner path to follow.
261 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
262 target : targetname of first spawnfunc_path_corner (starts here)
264 void spawnfunc_func_train()
267 objerror("func_train without a target");
271 InitMovingBrushTrigger();
272 self.effects |= EF_LOWPRECISION;
274 // wait for targets to spawn
275 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
280 void rotating_blocked()
283 if(self.dmg && other.takedamage != DAMAGE_NO) {
284 if(self.dmgtime2 < time) {
285 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
286 self.dmgtime2 = time + self.dmgtime;
289 // Gib dead/dying stuff
290 if(other.deadflag != DEAD_NO)
291 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
297 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
298 Brush model that spins in place on one axis (default Z).
299 speed : speed to rotate (in degrees per second)
300 noise : path/name of looping .wav file to play.
301 dmg : Do this mutch dmg every .dmgtime intervall when blocked
305 void spawnfunc_func_rotating()
309 precache_sound(self.noise);
310 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
314 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
315 if (self.spawnflags & 4) // X (untested)
316 self.avelocity = '0 0 1' * self.speed;
317 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
318 else if (self.spawnflags & 8) // Y (untested)
319 self.avelocity = '1 0 0' * self.speed;
320 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
322 self.avelocity = '0 1 0' * self.speed;
324 if(self.dmg & (!self.message))
325 self.message = " was in the wrong place.";
328 if(self.dmg && (!self.dmgtime))
331 self.dmgtime2 = time;
333 InitMovingBrushTrigger();
334 // no EF_LOWPRECISION here, as rounding angles is bad
336 self.blocked = rotating_blocked;
338 // wait for targets to spawn
339 self.nextthink = self.ltime + 999999999;
340 self.think = SUB_Null;
345 void func_bobbing_controller_think()
348 self.nextthink = time + 0.1;
349 // calculate sinewave using makevectors
350 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
351 v = self.owner.destvec + self.owner.movedir * v_forward_y;
352 // * 10 so it will arrive in 0.1 sec
353 self.owner.velocity = (v - self.owner.origin) * 10;
356 void bobbing_blocked()
358 // no need to duplicate code
362 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
363 Brush model that moves back and forth on one axis (default Z).
364 speed : how long one cycle takes in seconds (default 4)
365 height : how far the cycle moves (default 32)
366 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
367 noise : path/name of looping .wav file to play.
368 dmg : Do this mutch dmg every .dmgtime intervall when blocked
371 void spawnfunc_func_bobbing()
373 local entity controller;
376 precache_sound(self.noise);
377 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
383 // center of bobbing motion
384 self.destvec = self.origin;
385 // time scale to get degrees
386 self.cnt = 360 / self.speed;
388 // damage when blocked
389 self.blocked = bobbing_blocked;
390 if(self.dmg & (!self.message))
391 self.message = " was in the wrong place.";
392 if(self.dmg && (!self.dmgtime))
394 self.dmgtime2 = time;
397 if (self.spawnflags & 1) // X
398 self.movedir = '1 0 0' * self.height;
399 else if (self.spawnflags & 2) // Y
400 self.movedir = '0 1 0' * self.height;
402 self.movedir = '0 0 1' * self.height;
404 InitMovingBrushTrigger();
406 // wait for targets to spawn
407 controller = spawn();
408 controller.classname = "func_bobbing_controller";
409 controller.owner = self;
410 controller.nextthink = time + 1;
411 controller.think = func_bobbing_controller_think;
412 self.nextthink = self.ltime + 999999999;
413 self.think = SUB_Null;
415 // Savage: Reduce bandwith, critical on e.g. nexdm02
416 self.effects |= EF_LOWPRECISION;
419 // button and multiple button
422 void() button_return;
426 self.state = STATE_TOP;
427 self.nextthink = self.ltime + self.wait;
428 self.think = button_return;
429 activator = self.enemy;
431 self.frame = 1; // use alternate textures
436 self.state = STATE_BOTTOM;
441 self.state = STATE_DOWN;
442 SUB_CalcMove (self.pos1, self.speed, button_done);
443 self.frame = 0; // use normal textures
445 self.takedamage = DAMAGE_YES; // can be shot again
449 void button_blocked()
451 // do nothing, just don't come all the way back out
457 self.health = self.max_health;
458 self.takedamage = DAMAGE_NO; // will be reset upon return
460 if (self.state == STATE_UP || self.state == STATE_TOP)
463 if (self.noise != "")
464 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
466 self.state = STATE_UP;
467 SUB_CalcMove (self.pos2, self.speed, button_wait);
473 // if (activator.classname != "player")
475 // dprint(activator.classname);
476 // dprint(" triggered a button\n");
478 self.enemy = activator;
484 // if (activator.classname != "player")
486 // dprint(activator.classname);
487 // dprint(" touched a button\n");
491 if (other.classname != "player")
495 self.enemy = other.owner;
499 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
501 self.health = self.health - damage;
502 if (self.health <= 0)
504 // if (activator.classname != "player")
506 // dprint(activator.classname);
507 // dprint(" killed a button\n");
509 self.enemy = damage_attacker;
515 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
516 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.
518 "angle" determines the opening direction
519 "target" all entities with a matching targetname will be used
520 "speed" override the default 40 speed
521 "wait" override the default 1 second wait (-1 = never return)
522 "lip" override the default 4 pixel lip remaining at end of move
523 "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
530 void spawnfunc_func_button()
534 InitMovingBrushTrigger();
535 self.effects |= EF_LOWPRECISION;
537 self.blocked = button_blocked;
538 self.use = button_use;
540 // if (self.health == 0) // all buttons are now shootable
544 self.max_health = self.health;
545 self.event_damage = button_damage;
546 self.takedamage = DAMAGE_YES;
549 self.touch = button_touch;
559 precache_sound(self.noise);
561 self.state = STATE_BOTTOM;
563 self.pos1 = self.origin;
564 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
568 float DOOR_START_OPEN = 1;
569 float DOOR_DONT_LINK = 4;
570 float DOOR_TOGGLE = 32;
574 Doors are similar to buttons, but can spawn a fat trigger field around them
575 to open without a touch, and they link together to form simultanious
578 Door.owner is the master door. If there is only one door, it points to itself.
579 If multiple doors, all will point to a single one.
581 Door.enemy chains from the master door through all doors linked in the chain.
586 =============================================================================
590 =============================================================================
599 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
600 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
603 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
604 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
606 //Dont chamge direction for dead or dying stuff
607 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
610 if (self.state == STATE_DOWN)
616 //gib dying stuff just to make sure
617 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
618 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
622 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
623 // if a door has a negative wait, it would never come back if blocked,
624 // so let it just squash the object to death real fast
625 /* if (self.wait >= 0)
627 if (self.state == STATE_DOWN)
638 if (self.noise1 != "")
639 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
640 self.state = STATE_TOP;
641 if (self.spawnflags & DOOR_TOGGLE)
642 return; // don't come down automatically
643 self.think = door_go_down;
644 self.nextthink = self.ltime + self.wait;
647 void door_hit_bottom()
649 if (self.noise1 != "")
650 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
651 self.state = STATE_BOTTOM;
656 if (self.noise2 != "")
657 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
660 self.takedamage = DAMAGE_YES;
661 self.health = self.max_health;
664 self.state = STATE_DOWN;
665 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
670 if (self.state == STATE_UP)
671 return; // allready going up
673 if (self.state == STATE_TOP)
674 { // reset top wait time
675 self.nextthink = self.ltime + self.wait;
679 if (self.noise2 != "")
680 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
681 self.state = STATE_UP;
682 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
689 =============================================================================
693 =============================================================================
702 if (self.owner != self)
703 objerror ("door_fire: self.owner != self");
705 oldmessage = self.message;
706 self.message = ""; // no more message
709 if (self.spawnflags & DOOR_TOGGLE)
711 if (self.state == STATE_UP || self.state == STATE_TOP)
718 } while ( (self != starte) && (self != world) );
724 // trigger all paired doors
730 } while ( (self != starte) && (self != world) );
733 self.message = oldmessage;
741 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
752 void door_trigger_touch()
754 if (other.health < 1)
757 if (time < self.attack_finished_single)
759 self.attack_finished_single = time + 1;
768 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
771 self.health = self.health - damage;
772 if (self.health <= 0)
776 self.health = self.max_health;
777 self.takedamage = DAMAGE_NO; // wil be reset upon return
793 if (other.classname != "player")
795 if (self.owner.attack_finished_single > time)
798 self.owner.attack_finished_single = time + 2;
800 if (self.owner.message != "")
802 if (other.flags & FL_CLIENT)
803 centerprint (other, self.owner.message);
804 play2(other, "misc/talk.wav");
809 =============================================================================
813 =============================================================================
817 entity spawn_field(vector fmins, vector fmaxs)
819 local entity trigger;
823 trigger.classname = "doortriggerfield";
824 trigger.movetype = MOVETYPE_NONE;
825 trigger.solid = SOLID_TRIGGER;
826 trigger.owner = self;
827 trigger.touch = door_trigger_touch;
831 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
836 float EntitiesTouching(entity e1, entity e2)
838 if (e1.mins_x > e2.maxs_x)
840 if (e1.mins_y > e2.maxs_y)
842 if (e1.mins_z > e2.maxs_z)
844 if (e1.maxs_x < e2.mins_x)
846 if (e1.maxs_y < e2.mins_y)
848 if (e1.maxs_z < e2.mins_z)
863 local entity t, starte;
864 local vector cmins, cmaxs;
867 return; // already linked by another door
868 if (self.spawnflags & 4)
870 self.owner = self.enemy = self;
871 return; // don't want to link this door
882 self.owner = starte; // master door
885 starte.health = self.health;
887 starte.targetname = self.targetname;
888 if (self.message != "")
889 starte.message = self.message;
891 t = find(t, classname, self.classname);
894 self.enemy = starte; // make the chain a loop
896 // shootable, or triggered doors just needed the owner/enemy links,
897 // they don't spawn a field
908 self.owner.trigger_field = spawn_field(cmins, cmaxs);
913 if (EntitiesTouching(self,t))
916 objerror ("cross connected doors");
921 if (t.mins_x < cmins_x)
923 if (t.mins_y < cmins_y)
925 if (t.mins_z < cmins_z)
927 if (t.maxs_x > cmaxs_x)
929 if (t.maxs_y > cmaxs_y)
931 if (t.maxs_z > cmaxs_z)
939 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
940 if two doors touch, they are assumed to be connected and operate as a unit.
942 TOGGLE causes the door to wait in both the start and end states for a trigger event.
944 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).
946 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
947 "angle" determines the opening direction
948 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
949 "health" if set, door must be shot open
950 "speed" movement speed (100 default)
951 "wait" wait before returning (3 default, -1 = never return)
952 "lip" lip remaining at end of move (8 default)
953 "dmg" damage to inflict when blocked (2 default)
960 FIXME: only one sound set available at the time being
964 void spawnfunc_func_door()
966 //if (!self.deathtype) // map makers can override this
967 // self.deathtype = " got in the way";
970 self.max_health = self.health;
971 InitMovingBrushTrigger();
972 self.effects |= EF_LOWPRECISION;
973 self.classname = "door";
975 self.blocked = door_blocked;
978 if(self.spawnflags & 8)
981 if(self.dmg & (!self.message))
982 self.message = "was in the wrong place.";
988 precache_sound ("plats/medplat1.wav");
989 precache_sound ("plats/medplat2.wav");
990 self.noise2 = "plats/medplat1.wav";
991 self.noise1 = "plats/medplat2.wav";
1003 self.pos1 = self.origin;
1004 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1006 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1007 // but spawn in the open position
1008 if (self.spawnflags & DOOR_START_OPEN)
1010 setorigin (self, self.pos2);
1011 self.pos2 = self.pos1;
1012 self.pos1 = self.origin;
1015 self.state = STATE_BOTTOM;
1019 self.takedamage = DAMAGE_YES;
1020 self.event_damage = door_damage;
1026 self.touch = door_touch;
1028 // LinkDoors can't be done until all of the doors have been spawned, so
1029 // the sizes can be detected properly.
1030 InitializeEntity(self, LinkDoors, INITPRIO_FINDTARGET);
1034 =============================================================================
1038 =============================================================================
1041 void() fd_secret_move1;
1042 void() fd_secret_move2;
1043 void() fd_secret_move3;
1044 void() fd_secret_move4;
1045 void() fd_secret_move5;
1046 void() fd_secret_move6;
1047 void() fd_secret_done;
1049 float SECRET_OPEN_ONCE = 1; // stays open
1050 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1051 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1052 float SECRET_NO_SHOOT = 8; // only opened by trigger
1053 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1056 void fd_secret_use()
1059 string message_save;
1061 self.health = 10000;
1062 self.bot_attack = TRUE;
1064 // exit if still moving around...
1065 if (self.origin != self.oldorigin)
1068 message_save = self.message;
1069 self.message = ""; // no more message
1070 SUB_UseTargets(); // fire all targets / killtargets
1071 self.message = message_save;
1073 self.velocity = '0 0 0';
1075 // Make a sound, wait a little...
1077 if (self.noise1 != "")
1078 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1079 self.nextthink = self.ltime + 0.1;
1081 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1082 makevectors(self.mangle);
1086 if (self.spawnflags & SECRET_1ST_DOWN)
1087 self.t_width = fabs(v_up * self.size);
1089 self.t_width = fabs(v_right * self.size);
1093 self.t_length = fabs(v_forward * self.size);
1095 if (self.spawnflags & SECRET_1ST_DOWN)
1096 self.dest1 = self.origin - v_up * self.t_width;
1098 self.dest1 = self.origin + v_right * (self.t_width * temp);
1100 self.dest2 = self.dest1 + v_forward * self.t_length;
1101 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1102 if (self.noise2 != "")
1103 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1106 // Wait after first movement...
1107 void fd_secret_move1()
1109 self.nextthink = self.ltime + 1.0;
1110 self.think = fd_secret_move2;
1111 if (self.noise3 != "")
1112 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1115 // Start moving sideways w/sound...
1116 void fd_secret_move2()
1118 if (self.noise2 != "")
1119 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1120 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1123 // Wait here until time to go back...
1124 void fd_secret_move3()
1126 if (self.noise3 != "")
1127 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1128 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1130 self.nextthink = self.ltime + self.wait;
1131 self.think = fd_secret_move4;
1136 void fd_secret_move4()
1138 if (self.noise2 != "")
1139 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1140 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1144 void fd_secret_move5()
1146 self.nextthink = self.ltime + 1.0;
1147 self.think = fd_secret_move6;
1148 if (self.noise3 != "")
1149 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1152 void fd_secret_move6()
1154 if (self.noise2 != "")
1155 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1156 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1159 void fd_secret_done()
1161 if (self.spawnflags&SECRET_YES_SHOOT)
1163 self.health = 10000;
1164 self.takedamage = DAMAGE_YES;
1165 //self.th_pain = fd_secret_use;
1167 if (self.noise3 != "")
1168 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1171 void secret_blocked()
1173 if (time < self.attack_finished_single)
1175 self.attack_finished_single = time + 0.5;
1176 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1188 if (activator.classname != "player")
1190 if (self.attack_finished_single > time)
1193 self.attack_finished_single = time + 2;
1197 if (other.flags & FL_CLIENT)
1198 centerprint (other, self.message);
1199 play2(other, "misc/talk.wav");
1204 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1205 Basic secret door. Slides back, then to the side. Angle determines direction.
1206 wait = # of seconds before coming back
1207 1st_left = 1st move is left of arrow
1208 1st_down = 1st move is down from arrow
1209 always_shoot = even if targeted, keep shootable
1210 t_width = override WIDTH to move back (or height if going down)
1211 t_length = override LENGTH to move sideways
1212 "dmg" damage to inflict when blocked (2 default)
1214 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1221 void spawnfunc_func_door_secret()
1223 /*if (!self.deathtype) // map makers can override this
1224 self.deathtype = " got in the way";*/
1230 self.mangle = self.angles;
1231 self.angles = '0 0 0';
1232 self.classname = "door";
1233 InitMovingBrushTrigger();
1234 self.effects |= EF_LOWPRECISION;
1236 self.touch = secret_touch;
1237 self.blocked = secret_blocked;
1239 self.use = fd_secret_use;
1241 self.spawnflags |= SECRET_YES_SHOOT;
1243 if(self.spawnflags&SECRET_YES_SHOOT)
1245 self.health = 10000;
1246 self.takedamage = DAMAGE_YES;
1247 self.event_damage = fd_secret_use;
1249 self.oldorigin = self.origin;
1251 self.wait = 5; // 5 seconds before closing