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;
244 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
246 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
249 void func_train_find()
252 targ = find(world, targetname, self.target);
253 self.target = targ.target;
255 objerror("func_train_find: no next target");
256 setorigin(self, targ.origin - self.mins);
257 self.nextthink = self.ltime + 1;
258 self.think = train_next;
261 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
262 Ridable platform, targets spawnfunc_path_corner path to follow.
263 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
264 target : targetname of first spawnfunc_path_corner (starts here)
266 void spawnfunc_func_train()
269 objerror("func_train without a target");
273 InitMovingBrushTrigger();
274 self.effects |= EF_LOWPRECISION;
276 // wait for targets to spawn
277 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
282 void rotating_blocked()
285 if(self.dmg && other.takedamage != DAMAGE_NO) {
286 if(self.dmgtime2 < time) {
287 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
288 self.dmgtime2 = time + self.dmgtime;
291 // Gib dead/dying stuff
292 if(other.deadflag != DEAD_NO)
293 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
299 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
300 Brush model that spins in place on one axis (default Z).
301 speed : speed to rotate (in degrees per second)
302 noise : path/name of looping .wav file to play.
303 dmg : Do this mutch dmg every .dmgtime intervall when blocked
307 void spawnfunc_func_rotating()
311 precache_sound(self.noise);
312 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
316 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
317 if (self.spawnflags & 4) // X (untested)
318 self.avelocity = '0 0 1' * self.speed;
319 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
320 else if (self.spawnflags & 8) // Y (untested)
321 self.avelocity = '1 0 0' * self.speed;
322 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
324 self.avelocity = '0 1 0' * self.speed;
326 if(self.dmg & (!self.message))
327 self.message = " was in the wrong place.";
330 if(self.dmg && (!self.dmgtime))
333 self.dmgtime2 = time;
335 InitMovingBrushTrigger();
336 // no EF_LOWPRECISION here, as rounding angles is bad
338 self.blocked = rotating_blocked;
340 // wait for targets to spawn
341 self.nextthink = self.ltime + 999999999;
342 self.think = SUB_Null;
347 void func_bobbing_controller_think()
350 self.nextthink = time + 0.1;
351 // calculate sinewave using makevectors
352 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
353 v = self.owner.destvec + self.owner.movedir * v_forward_y;
354 // * 10 so it will arrive in 0.1 sec
355 self.owner.velocity = (v - self.owner.origin) * 10;
358 void bobbing_blocked()
360 // no need to duplicate code
364 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
365 Brush model that moves back and forth on one axis (default Z).
366 speed : how long one cycle takes in seconds (default 4)
367 height : how far the cycle moves (default 32)
368 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
369 noise : path/name of looping .wav file to play.
370 dmg : Do this mutch dmg every .dmgtime intervall when blocked
373 void spawnfunc_func_bobbing()
375 local entity controller;
378 precache_sound(self.noise);
379 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
385 // center of bobbing motion
386 self.destvec = self.origin;
387 // time scale to get degrees
388 self.cnt = 360 / self.speed;
390 // damage when blocked
391 self.blocked = bobbing_blocked;
392 if(self.dmg & (!self.message))
393 self.message = " was in the wrong place.";
394 if(self.dmg && (!self.dmgtime))
396 self.dmgtime2 = time;
399 if (self.spawnflags & 1) // X
400 self.movedir = '1 0 0' * self.height;
401 else if (self.spawnflags & 2) // Y
402 self.movedir = '0 1 0' * self.height;
404 self.movedir = '0 0 1' * self.height;
406 InitMovingBrushTrigger();
408 // wait for targets to spawn
409 controller = spawn();
410 controller.classname = "func_bobbing_controller";
411 controller.owner = self;
412 controller.nextthink = time + 1;
413 controller.think = func_bobbing_controller_think;
414 self.nextthink = self.ltime + 999999999;
415 self.think = SUB_Null;
417 // Savage: Reduce bandwith, critical on e.g. nexdm02
418 self.effects |= EF_LOWPRECISION;
421 // button and multiple button
424 void() button_return;
428 self.state = STATE_TOP;
429 self.nextthink = self.ltime + self.wait;
430 self.think = button_return;
431 activator = self.enemy;
433 self.frame = 1; // use alternate textures
438 self.state = STATE_BOTTOM;
443 self.state = STATE_DOWN;
444 SUB_CalcMove (self.pos1, self.speed, button_done);
445 self.frame = 0; // use normal textures
447 self.takedamage = DAMAGE_YES; // can be shot again
451 void button_blocked()
453 // do nothing, just don't come all the way back out
459 self.health = self.max_health;
460 self.takedamage = DAMAGE_NO; // will be reset upon return
462 if (self.state == STATE_UP || self.state == STATE_TOP)
465 if (self.noise != "")
466 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
468 self.state = STATE_UP;
469 SUB_CalcMove (self.pos2, self.speed, button_wait);
475 // if (activator.classname != "player")
477 // dprint(activator.classname);
478 // dprint(" triggered a button\n");
480 self.enemy = activator;
486 // if (activator.classname != "player")
488 // dprint(activator.classname);
489 // dprint(" touched a button\n");
493 if (other.classname != "player")
497 self.enemy = other.owner;
501 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
503 self.health = self.health - damage;
504 if (self.health <= 0)
506 // if (activator.classname != "player")
508 // dprint(activator.classname);
509 // dprint(" killed a button\n");
511 self.enemy = damage_attacker;
517 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
518 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.
520 "angle" determines the opening direction
521 "target" all entities with a matching targetname will be used
522 "speed" override the default 40 speed
523 "wait" override the default 1 second wait (-1 = never return)
524 "lip" override the default 4 pixel lip remaining at end of move
525 "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
532 void spawnfunc_func_button()
536 InitMovingBrushTrigger();
537 self.effects |= EF_LOWPRECISION;
539 self.blocked = button_blocked;
540 self.use = button_use;
542 // if (self.health == 0) // all buttons are now shootable
546 self.max_health = self.health;
547 self.event_damage = button_damage;
548 self.takedamage = DAMAGE_YES;
551 self.touch = button_touch;
561 precache_sound(self.noise);
563 self.state = STATE_BOTTOM;
565 self.pos1 = self.origin;
566 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
570 float DOOR_START_OPEN = 1;
571 float DOOR_DONT_LINK = 4;
572 float DOOR_TOGGLE = 32;
576 Doors are similar to buttons, but can spawn a fat trigger field around them
577 to open without a touch, and they link together to form simultanious
580 Door.owner is the master door. If there is only one door, it points to itself.
581 If multiple doors, all will point to a single one.
583 Door.enemy chains from the master door through all doors linked in the chain.
588 =============================================================================
592 =============================================================================
601 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
602 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
605 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
606 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
608 //Dont chamge direction for dead or dying stuff
609 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
612 if (self.state == STATE_DOWN)
618 //gib dying stuff just to make sure
619 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
620 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
624 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
625 // if a door has a negative wait, it would never come back if blocked,
626 // so let it just squash the object to death real fast
627 /* if (self.wait >= 0)
629 if (self.state == STATE_DOWN)
640 if (self.noise1 != "")
641 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
642 self.state = STATE_TOP;
643 if (self.spawnflags & DOOR_TOGGLE)
644 return; // don't come down automatically
645 self.think = door_go_down;
646 self.nextthink = self.ltime + self.wait;
649 void door_hit_bottom()
651 if (self.noise1 != "")
652 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
653 self.state = STATE_BOTTOM;
658 if (self.noise2 != "")
659 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
662 self.takedamage = DAMAGE_YES;
663 self.health = self.max_health;
666 self.state = STATE_DOWN;
667 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
672 if (self.state == STATE_UP)
673 return; // allready going up
675 if (self.state == STATE_TOP)
676 { // reset top wait time
677 self.nextthink = self.ltime + self.wait;
681 if (self.noise2 != "")
682 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
683 self.state = STATE_UP;
684 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
691 =============================================================================
695 =============================================================================
704 if (self.owner != self)
705 objerror ("door_fire: self.owner != self");
707 oldmessage = self.message;
708 self.message = ""; // no more message
711 if (self.spawnflags & DOOR_TOGGLE)
713 if (self.state == STATE_UP || self.state == STATE_TOP)
720 } while ( (self != starte) && (self != world) );
726 // trigger all paired doors
732 } while ( (self != starte) && (self != world) );
735 self.message = oldmessage;
743 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
754 void door_trigger_touch()
756 if (other.health < 1)
759 if (time < self.attack_finished_single)
761 self.attack_finished_single = time + 1;
770 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
773 self.health = self.health - damage;
774 if (self.health <= 0)
778 self.health = self.max_health;
779 self.takedamage = DAMAGE_NO; // wil be reset upon return
795 if (other.classname != "player")
797 if (self.owner.attack_finished_single > time)
800 self.owner.attack_finished_single = time + 2;
802 if (self.owner.message != "")
804 if (other.flags & FL_CLIENT)
805 centerprint (other, self.owner.message);
806 play2(other, "misc/talk.wav");
811 =============================================================================
815 =============================================================================
819 entity spawn_field(vector fmins, vector fmaxs)
821 local entity trigger;
825 trigger.classname = "doortriggerfield";
826 trigger.movetype = MOVETYPE_NONE;
827 trigger.solid = SOLID_TRIGGER;
828 trigger.owner = self;
829 trigger.touch = door_trigger_touch;
833 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
838 float EntitiesTouching(entity e1, entity e2)
840 if (e1.mins_x > e2.maxs_x)
842 if (e1.mins_y > e2.maxs_y)
844 if (e1.mins_z > e2.maxs_z)
846 if (e1.maxs_x < e2.mins_x)
848 if (e1.maxs_y < e2.mins_y)
850 if (e1.maxs_z < e2.mins_z)
865 local entity t, starte;
866 local vector cmins, cmaxs;
869 return; // already linked by another door
870 if (self.spawnflags & 4)
872 self.owner = self.enemy = self;
873 return; // don't want to link this door
884 self.owner = starte; // master door
887 starte.health = self.health;
889 starte.targetname = self.targetname;
890 if (self.message != "")
891 starte.message = self.message;
893 t = find(t, classname, self.classname);
896 self.enemy = starte; // make the chain a loop
898 // shootable, or triggered doors just needed the owner/enemy links,
899 // they don't spawn a field
910 self.owner.trigger_field = spawn_field(cmins, cmaxs);
915 if (EntitiesTouching(self,t))
918 objerror ("cross connected doors");
923 if (t.mins_x < cmins_x)
925 if (t.mins_y < cmins_y)
927 if (t.mins_z < cmins_z)
929 if (t.maxs_x > cmaxs_x)
931 if (t.maxs_y > cmaxs_y)
933 if (t.maxs_z > cmaxs_z)
941 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
942 if two doors touch, they are assumed to be connected and operate as a unit.
944 TOGGLE causes the door to wait in both the start and end states for a trigger event.
946 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).
948 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
949 "angle" determines the opening direction
950 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
951 "health" if set, door must be shot open
952 "speed" movement speed (100 default)
953 "wait" wait before returning (3 default, -1 = never return)
954 "lip" lip remaining at end of move (8 default)
955 "dmg" damage to inflict when blocked (2 default)
962 FIXME: only one sound set available at the time being
966 void door_init_startopen()
968 setorigin (self, self.pos2);
969 self.pos2 = self.pos1;
970 self.pos1 = self.origin;
973 void spawnfunc_func_door()
975 //if (!self.deathtype) // map makers can override this
976 // self.deathtype = " got in the way";
979 self.max_health = self.health;
980 InitMovingBrushTrigger();
981 self.effects |= EF_LOWPRECISION;
982 self.classname = "door";
984 self.blocked = door_blocked;
987 if(self.spawnflags & 8)
990 if(self.dmg & (!self.message))
991 self.message = "was in the wrong place.";
997 precache_sound ("plats/medplat1.wav");
998 precache_sound ("plats/medplat2.wav");
999 self.noise2 = "plats/medplat1.wav";
1000 self.noise1 = "plats/medplat2.wav";
1012 self.pos1 = self.origin;
1013 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1015 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1016 // but spawn in the open position
1017 if (self.spawnflags & DOOR_START_OPEN)
1018 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1020 self.state = STATE_BOTTOM;
1024 self.takedamage = DAMAGE_YES;
1025 self.event_damage = door_damage;
1031 self.touch = door_touch;
1033 // LinkDoors can't be done until all of the doors have been spawned, so
1034 // the sizes can be detected properly.
1035 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1039 =============================================================================
1043 =============================================================================
1046 void() fd_secret_move1;
1047 void() fd_secret_move2;
1048 void() fd_secret_move3;
1049 void() fd_secret_move4;
1050 void() fd_secret_move5;
1051 void() fd_secret_move6;
1052 void() fd_secret_done;
1054 float SECRET_OPEN_ONCE = 1; // stays open
1055 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1056 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1057 float SECRET_NO_SHOOT = 8; // only opened by trigger
1058 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1061 void fd_secret_use()
1064 string message_save;
1066 self.health = 10000;
1067 self.bot_attack = TRUE;
1069 // exit if still moving around...
1070 if (self.origin != self.oldorigin)
1073 message_save = self.message;
1074 self.message = ""; // no more message
1075 SUB_UseTargets(); // fire all targets / killtargets
1076 self.message = message_save;
1078 self.velocity = '0 0 0';
1080 // Make a sound, wait a little...
1082 if (self.noise1 != "")
1083 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1084 self.nextthink = self.ltime + 0.1;
1086 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1087 makevectors(self.mangle);
1091 if (self.spawnflags & SECRET_1ST_DOWN)
1092 self.t_width = fabs(v_up * self.size);
1094 self.t_width = fabs(v_right * self.size);
1098 self.t_length = fabs(v_forward * self.size);
1100 if (self.spawnflags & SECRET_1ST_DOWN)
1101 self.dest1 = self.origin - v_up * self.t_width;
1103 self.dest1 = self.origin + v_right * (self.t_width * temp);
1105 self.dest2 = self.dest1 + v_forward * self.t_length;
1106 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1107 if (self.noise2 != "")
1108 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1111 // Wait after first movement...
1112 void fd_secret_move1()
1114 self.nextthink = self.ltime + 1.0;
1115 self.think = fd_secret_move2;
1116 if (self.noise3 != "")
1117 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1120 // Start moving sideways w/sound...
1121 void fd_secret_move2()
1123 if (self.noise2 != "")
1124 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1125 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1128 // Wait here until time to go back...
1129 void fd_secret_move3()
1131 if (self.noise3 != "")
1132 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1133 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1135 self.nextthink = self.ltime + self.wait;
1136 self.think = fd_secret_move4;
1141 void fd_secret_move4()
1143 if (self.noise2 != "")
1144 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1145 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1149 void fd_secret_move5()
1151 self.nextthink = self.ltime + 1.0;
1152 self.think = fd_secret_move6;
1153 if (self.noise3 != "")
1154 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1157 void fd_secret_move6()
1159 if (self.noise2 != "")
1160 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1161 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1164 void fd_secret_done()
1166 if (self.spawnflags&SECRET_YES_SHOOT)
1168 self.health = 10000;
1169 self.takedamage = DAMAGE_YES;
1170 //self.th_pain = fd_secret_use;
1172 if (self.noise3 != "")
1173 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1176 void secret_blocked()
1178 if (time < self.attack_finished_single)
1180 self.attack_finished_single = time + 0.5;
1181 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1193 if (activator.classname != "player")
1195 if (self.attack_finished_single > time)
1198 self.attack_finished_single = time + 2;
1202 if (other.flags & FL_CLIENT)
1203 centerprint (other, self.message);
1204 play2(other, "misc/talk.wav");
1209 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1210 Basic secret door. Slides back, then to the side. Angle determines direction.
1211 wait = # of seconds before coming back
1212 1st_left = 1st move is left of arrow
1213 1st_down = 1st move is down from arrow
1214 always_shoot = even if targeted, keep shootable
1215 t_width = override WIDTH to move back (or height if going down)
1216 t_length = override LENGTH to move sideways
1217 "dmg" damage to inflict when blocked (2 default)
1219 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1226 void spawnfunc_func_door_secret()
1228 /*if (!self.deathtype) // map makers can override this
1229 self.deathtype = " got in the way";*/
1235 self.mangle = self.angles;
1236 self.angles = '0 0 0';
1237 self.classname = "door";
1238 InitMovingBrushTrigger();
1239 self.effects |= EF_LOWPRECISION;
1241 self.touch = secret_touch;
1242 self.blocked = secret_blocked;
1244 self.use = fd_secret_use;
1246 self.spawnflags |= SECRET_YES_SHOOT;
1248 if(self.spawnflags&SECRET_YES_SHOOT)
1250 self.health = 10000;
1251 self.takedamage = DAMAGE_YES;
1252 self.event_damage = fd_secret_use;
1254 self.oldorigin = self.origin;
1256 self.wait = 5; // 5 seconds before closing