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.absmin + '25 25 0';
31 tmax = self.absmax - '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 not(other.iscreature)
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 not(other.iscreature)
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 squished";
167 if(self.dmg && (!self.message2))
168 self.message2 = "was squished by";
170 if (self.sounds == 1)
172 precache_sound ("plats/plat1.wav");
173 precache_sound ("plats/plat2.wav");
174 self.noise = "plats/plat1.wav";
175 self.noise1 = "plats/plat2.wav";
178 if (self.sounds == 2)
180 precache_sound ("plats/medplat1.wav");
181 precache_sound ("plats/medplat2.wav");
182 self.noise = "plats/medplat1.wav";
183 self.noise1 = "plats/medplat2.wav";
188 precache_sound (self.sound1);
189 self.noise = self.sound1;
193 precache_sound (self.sound2);
194 self.noise1 = self.sound2;
197 self.mangle = self.angles;
198 self.angles = '0 0 0';
200 self.classname = "plat";
201 InitMovingBrushTrigger();
202 self.effects |= EF_LOWPRECISION;
203 setsize (self, self.mins , self.maxs);
205 self.blocked = plat_crush;
210 self.pos1 = self.origin;
211 self.pos2 = self.origin;
212 self.pos2_z = self.origin_z - self.size_z + 8;
214 self.use = plat_trigger_use;
216 plat_spawn_inside_trigger (); // the "start moving" trigger
224 InitializeEntity(self, plat_init_movedown, INITPRIO_SETLOCATION);
231 self.think = train_next;
232 self.nextthink = self.ltime + self.wait;
238 targ = find(world, targetname, self.target);
239 self.target = targ.target;
241 objerror("train_next: no next target");
242 self.wait = targ.wait;
248 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
250 SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
255 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
257 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
261 void func_train_find()
264 targ = find(world, targetname, self.target);
265 self.target = targ.target;
267 objerror("func_train_find: no next target");
268 setorigin(self, targ.origin - self.mins);
269 self.nextthink = self.ltime + 1;
270 self.think = train_next;
273 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
274 Ridable platform, targets spawnfunc_path_corner path to follow.
275 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
276 target : targetname of first spawnfunc_path_corner (starts here)
278 void spawnfunc_func_train()
281 objerror("func_train without a target");
285 InitMovingBrushTrigger();
286 self.effects |= EF_LOWPRECISION;
288 // wait for targets to spawn
289 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
294 void rotating_blocked()
297 if(self.dmg && other.takedamage != DAMAGE_NO) {
298 if(self.dmgtime2 < time) {
299 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
300 self.dmgtime2 = time + self.dmgtime;
303 // Gib dead/dying stuff
304 if(other.deadflag != DEAD_NO)
305 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
311 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
312 Brush model that spins in place on one axis (default Z).
313 speed : speed to rotate (in degrees per second)
314 noise : path/name of looping .wav file to play.
315 dmg : Do this mutch dmg every .dmgtime intervall when blocked
319 void spawnfunc_func_rotating()
323 precache_sound(self.noise);
324 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
328 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
329 if (self.spawnflags & 4) // X (untested)
330 self.avelocity = '0 0 1' * self.speed;
331 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
332 else if (self.spawnflags & 8) // Y (untested)
333 self.avelocity = '1 0 0' * self.speed;
334 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
336 self.avelocity = '0 1 0' * self.speed;
338 if(self.dmg & (!self.message))
339 self.message = " was squished";
340 if(self.dmg && (!self.message2))
341 self.message2 = "was squished by";
344 if(self.dmg && (!self.dmgtime))
347 self.dmgtime2 = time;
349 InitMovingBrushTrigger();
350 // no EF_LOWPRECISION here, as rounding angles is bad
352 self.blocked = rotating_blocked;
354 // wait for targets to spawn
355 self.nextthink = self.ltime + 999999999;
356 self.think = SUB_Null;
360 void func_bobbing_controller_think()
363 self.nextthink = time + 0.1;
364 // calculate sinewave using makevectors
365 makevectors((time * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
366 v = self.owner.destvec + self.owner.movedir * v_forward_y;
367 // * 10 so it will arrive in 0.1 sec
368 self.owner.velocity = (v - self.owner.origin) * 10;
371 void bobbing_blocked()
373 // no need to duplicate code
377 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
378 Brush model that moves back and forth on one axis (default Z).
379 speed : how long one cycle takes in seconds (default 4)
380 height : how far the cycle moves (default 32)
381 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
382 noise : path/name of looping .wav file to play.
383 dmg : Do this mutch dmg every .dmgtime intervall when blocked
386 void spawnfunc_func_bobbing()
388 local entity controller;
391 precache_sound(self.noise);
392 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
398 // center of bobbing motion
399 self.destvec = self.origin;
400 // time scale to get degrees
401 self.cnt = 360 / self.speed;
403 // damage when blocked
404 self.blocked = bobbing_blocked;
405 if(self.dmg & (!self.message))
406 self.message = " was squished";
407 if(self.dmg && (!self.message2))
408 self.message2 = "was squished by";
409 if(self.dmg && (!self.dmgtime))
411 self.dmgtime2 = time;
414 if (self.spawnflags & 1) // X
415 self.movedir = '1 0 0' * self.height;
416 else if (self.spawnflags & 2) // Y
417 self.movedir = '0 1 0' * self.height;
419 self.movedir = '0 0 1' * self.height;
421 InitMovingBrushTrigger();
423 // wait for targets to spawn
424 controller = spawn();
425 controller.classname = "func_bobbing_controller";
426 controller.owner = self;
427 controller.nextthink = time + 1;
428 controller.think = func_bobbing_controller_think;
429 self.nextthink = self.ltime + 999999999;
430 self.think = SUB_Null;
432 // Savage: Reduce bandwith, critical on e.g. nexdm02
433 self.effects |= EF_LOWPRECISION;
436 // button and multiple button
439 void() button_return;
443 self.state = STATE_TOP;
444 self.nextthink = self.ltime + self.wait;
445 self.think = button_return;
446 activator = self.enemy;
448 self.frame = 1; // use alternate textures
453 self.state = STATE_BOTTOM;
458 self.state = STATE_DOWN;
459 SUB_CalcMove (self.pos1, self.speed, button_done);
460 self.frame = 0; // use normal textures
462 self.takedamage = DAMAGE_YES; // can be shot again
466 void button_blocked()
468 // do nothing, just don't come all the way back out
474 self.health = self.max_health;
475 self.takedamage = DAMAGE_NO; // will be reset upon return
477 if (self.state == STATE_UP || self.state == STATE_TOP)
480 if (self.noise != "")
481 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
483 self.state = STATE_UP;
484 SUB_CalcMove (self.pos2, self.speed, button_wait);
490 // if (activator.classname != "player")
492 // dprint(activator.classname);
493 // dprint(" triggered a button\n");
495 self.enemy = activator;
501 // if (activator.classname != "player")
503 // dprint(activator.classname);
504 // dprint(" touched a button\n");
508 if not(other.iscreature)
510 if(other.velocity * self.movedir < 0)
514 self.enemy = other.owner;
518 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
520 self.health = self.health - damage;
521 if (self.health <= 0)
523 // if (activator.classname != "player")
525 // dprint(activator.classname);
526 // dprint(" killed a button\n");
528 self.enemy = damage_attacker;
534 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
535 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.
537 "angle" determines the opening direction
538 "target" all entities with a matching targetname will be used
539 "speed" override the default 40 speed
540 "wait" override the default 1 second wait (-1 = never return)
541 "lip" override the default 4 pixel lip remaining at end of move
542 "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
549 void spawnfunc_func_button()
553 InitMovingBrushTrigger();
554 self.effects |= EF_LOWPRECISION;
556 self.blocked = button_blocked;
557 self.use = button_use;
559 // if (self.health == 0) // all buttons are now shootable
563 self.max_health = self.health;
564 self.event_damage = button_damage;
565 self.takedamage = DAMAGE_YES;
568 self.touch = button_touch;
578 precache_sound(self.noise);
580 self.state = STATE_BOTTOM;
582 self.pos1 = self.origin;
583 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
587 float DOOR_START_OPEN = 1;
588 float DOOR_DONT_LINK = 4;
589 float DOOR_TOGGLE = 32;
593 Doors are similar to buttons, but can spawn a fat trigger field around them
594 to open without a touch, and they link together to form simultanious
597 Door.owner is the master door. If there is only one door, it points to itself.
598 If multiple doors, all will point to a single one.
600 Door.enemy chains from the master door through all doors linked in the chain.
605 =============================================================================
609 =============================================================================
618 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
619 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
622 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
623 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
625 //Dont chamge direction for dead or dying stuff
626 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
629 if (self.state == STATE_DOWN)
635 //gib dying stuff just to make sure
636 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
637 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
641 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
642 // if a door has a negative wait, it would never come back if blocked,
643 // so let it just squash the object to death real fast
644 /* if (self.wait >= 0)
646 if (self.state == STATE_DOWN)
657 if (self.noise1 != "")
658 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
659 self.state = STATE_TOP;
660 if (self.spawnflags & DOOR_TOGGLE)
661 return; // don't come down automatically
662 self.think = door_go_down;
663 self.nextthink = self.ltime + self.wait;
666 void door_hit_bottom()
668 if (self.noise1 != "")
669 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
670 self.state = STATE_BOTTOM;
675 if (self.noise2 != "")
676 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
679 self.takedamage = DAMAGE_YES;
680 self.health = self.max_health;
683 self.state = STATE_DOWN;
684 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
689 if (self.state == STATE_UP)
690 return; // allready going up
692 if (self.state == STATE_TOP)
693 { // reset top wait time
694 self.nextthink = self.ltime + self.wait;
698 if (self.noise2 != "")
699 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
700 self.state = STATE_UP;
701 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
704 oldmessage = self.message;
707 self.message = oldmessage;
712 =============================================================================
716 =============================================================================
724 if (self.owner != self)
725 objerror ("door_fire: self.owner != self");
729 if (self.spawnflags & DOOR_TOGGLE)
731 if (self.state == STATE_UP || self.state == STATE_TOP)
738 } while ( (self != starte) && (self != world) );
744 // trigger all paired doors
750 } while ( (self != starte) && (self != world) );
759 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
770 void door_trigger_touch()
772 if (other.health < 1)
773 if not(other.iscreature && other.deadflag == DEAD_NO)
776 if (time < self.attack_finished_single)
778 self.attack_finished_single = time + 1;
787 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
790 self.health = self.health - damage;
791 if (self.health <= 0)
795 self.health = self.max_health;
796 self.takedamage = DAMAGE_NO; // wil be reset upon return
812 if(other.classname != "player")
814 if (self.owner.attack_finished_single > time)
817 self.owner.attack_finished_single = time + 2;
819 if (!(self.owner.dmg) && (self.owner.message != ""))
821 if (other.flags & FL_CLIENT)
822 centerprint (other, self.owner.message);
823 play2(other, "misc/talk.wav");
828 =============================================================================
832 =============================================================================
836 entity spawn_field(vector fmins, vector fmaxs)
838 local entity trigger;
842 trigger.classname = "doortriggerfield";
843 trigger.movetype = MOVETYPE_NONE;
844 trigger.solid = SOLID_TRIGGER;
845 trigger.owner = self;
846 trigger.touch = door_trigger_touch;
850 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
855 float EntitiesTouching(entity e1, entity e2)
857 if (e1.mins_x > e2.maxs_x)
859 if (e1.mins_y > e2.maxs_y)
861 if (e1.mins_z > e2.maxs_z)
863 if (e1.maxs_x < e2.mins_x)
865 if (e1.maxs_y < e2.mins_y)
867 if (e1.maxs_z < e2.mins_z)
882 local entity t, starte;
883 local vector cmins, cmaxs;
886 return; // already linked by another door
887 if (self.spawnflags & 4)
889 self.owner = self.enemy = self;
898 self.trigger_field = spawn_field(self.absmin, self.absmax);
900 return; // don't want to link this door
911 self.owner = starte; // master door
914 starte.health = self.health;
916 starte.targetname = self.targetname;
917 if (self.message != "")
918 starte.message = self.message;
920 t = find(t, classname, self.classname);
923 self.enemy = starte; // make the chain a loop
925 // shootable, or triggered doors just needed the owner/enemy links,
926 // they don't spawn a field
937 self.owner.trigger_field = spawn_field(cmins, cmaxs);
942 if (EntitiesTouching(self,t))
945 objerror ("cross connected doors");
950 if (t.absmin_x < cmins_x)
951 cmins_x = t.absmin_x;
952 if (t.absmin_y < cmins_y)
953 cmins_y = t.absmin_y;
954 if (t.absmin_z < cmins_z)
955 cmins_z = t.absmin_z;
956 if (t.absmax_x > cmaxs_x)
957 cmaxs_x = t.absmax_x;
958 if (t.absmax_y > cmaxs_y)
959 cmaxs_y = t.absmax_y;
960 if (t.absmax_z > cmaxs_z)
961 cmaxs_z = t.absmax_z;
968 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
969 if two doors touch, they are assumed to be connected and operate as a unit.
971 TOGGLE causes the door to wait in both the start and end states for a trigger event.
973 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).
975 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
976 "angle" determines the opening direction
977 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
978 "health" if set, door must be shot open
979 "speed" movement speed (100 default)
980 "wait" wait before returning (3 default, -1 = never return)
981 "lip" lip remaining at end of move (8 default)
982 "dmg" damage to inflict when blocked (2 default)
989 FIXME: only one sound set available at the time being
993 void door_init_startopen()
995 setorigin (self, self.pos2);
996 self.pos2 = self.pos1;
997 self.pos1 = self.origin;
1000 void spawnfunc_func_door()
1002 //if (!self.deathtype) // map makers can override this
1003 // self.deathtype = " got in the way";
1006 self.max_health = self.health;
1007 InitMovingBrushTrigger();
1008 self.effects |= EF_LOWPRECISION;
1009 self.classname = "door";
1011 self.blocked = door_blocked;
1012 self.use = door_use;
1014 if(self.spawnflags & 8)
1017 if(self.dmg && (!self.message))
1018 self.message = "was squished";
1019 if(self.dmg && (!self.message2))
1020 self.message2 = "was squished by";
1022 if (self.sounds > 0)
1024 precache_sound ("plats/medplat1.wav");
1025 precache_sound ("plats/medplat2.wav");
1026 self.noise2 = "plats/medplat1.wav";
1027 self.noise1 = "plats/medplat2.wav";
1037 self.pos1 = self.origin;
1038 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1040 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1041 // but spawn in the open position
1042 if (self.spawnflags & DOOR_START_OPEN)
1043 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1045 self.state = STATE_BOTTOM;
1049 self.takedamage = DAMAGE_YES;
1050 self.event_damage = door_damage;
1056 self.touch = door_touch;
1058 // LinkDoors can't be done until all of the doors have been spawned, so
1059 // the sizes can be detected properly.
1060 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1064 =============================================================================
1068 =============================================================================
1071 void() fd_secret_move1;
1072 void() fd_secret_move2;
1073 void() fd_secret_move3;
1074 void() fd_secret_move4;
1075 void() fd_secret_move5;
1076 void() fd_secret_move6;
1077 void() fd_secret_done;
1079 float SECRET_OPEN_ONCE = 1; // stays open
1080 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1081 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1082 float SECRET_NO_SHOOT = 8; // only opened by trigger
1083 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1086 void fd_secret_use()
1089 string message_save;
1091 self.health = 10000;
1092 self.bot_attack = TRUE;
1094 // exit if still moving around...
1095 if (self.origin != self.oldorigin)
1098 message_save = self.message;
1099 self.message = ""; // no more message
1100 SUB_UseTargets(); // fire all targets / killtargets
1101 self.message = message_save;
1103 self.velocity = '0 0 0';
1105 // Make a sound, wait a little...
1107 if (self.noise1 != "")
1108 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1109 self.nextthink = self.ltime + 0.1;
1111 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1112 makevectors(self.mangle);
1116 if (self.spawnflags & SECRET_1ST_DOWN)
1117 self.t_width = fabs(v_up * self.size);
1119 self.t_width = fabs(v_right * self.size);
1123 self.t_length = fabs(v_forward * self.size);
1125 if (self.spawnflags & SECRET_1ST_DOWN)
1126 self.dest1 = self.origin - v_up * self.t_width;
1128 self.dest1 = self.origin + v_right * (self.t_width * temp);
1130 self.dest2 = self.dest1 + v_forward * self.t_length;
1131 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1132 if (self.noise2 != "")
1133 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1136 // Wait after first movement...
1137 void fd_secret_move1()
1139 self.nextthink = self.ltime + 1.0;
1140 self.think = fd_secret_move2;
1141 if (self.noise3 != "")
1142 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1145 // Start moving sideways w/sound...
1146 void fd_secret_move2()
1148 if (self.noise2 != "")
1149 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1150 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1153 // Wait here until time to go back...
1154 void fd_secret_move3()
1156 if (self.noise3 != "")
1157 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1158 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1160 self.nextthink = self.ltime + self.wait;
1161 self.think = fd_secret_move4;
1166 void fd_secret_move4()
1168 if (self.noise2 != "")
1169 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1170 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1174 void fd_secret_move5()
1176 self.nextthink = self.ltime + 1.0;
1177 self.think = fd_secret_move6;
1178 if (self.noise3 != "")
1179 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1182 void fd_secret_move6()
1184 if (self.noise2 != "")
1185 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1186 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1189 void fd_secret_done()
1191 if (self.spawnflags&SECRET_YES_SHOOT)
1193 self.health = 10000;
1194 self.takedamage = DAMAGE_YES;
1195 //self.th_pain = fd_secret_use;
1197 if (self.noise3 != "")
1198 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1201 void secret_blocked()
1203 if (time < self.attack_finished_single)
1205 self.attack_finished_single = time + 0.5;
1206 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1218 if not(other.iscreature)
1220 if (self.attack_finished_single > time)
1223 self.attack_finished_single = time + 2;
1227 if (other.flags & FL_CLIENT)
1228 centerprint (other, self.message);
1229 play2(other, "misc/talk.wav");
1234 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1235 Basic secret door. Slides back, then to the side. Angle determines direction.
1236 wait = # of seconds before coming back
1237 1st_left = 1st move is left of arrow
1238 1st_down = 1st move is down from arrow
1239 always_shoot = even if targeted, keep shootable
1240 t_width = override WIDTH to move back (or height if going down)
1241 t_length = override LENGTH to move sideways
1242 "dmg" damage to inflict when blocked (2 default)
1244 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1251 void spawnfunc_func_door_secret()
1253 /*if (!self.deathtype) // map makers can override this
1254 self.deathtype = " got in the way";*/
1260 self.mangle = self.angles;
1261 self.angles = '0 0 0';
1262 self.classname = "door";
1263 InitMovingBrushTrigger();
1264 self.effects |= EF_LOWPRECISION;
1266 self.touch = secret_touch;
1267 self.blocked = secret_blocked;
1269 self.use = fd_secret_use;
1274 self.spawnflags |= SECRET_YES_SHOOT;
1276 if(self.spawnflags&SECRET_YES_SHOOT)
1278 self.health = 10000;
1279 self.takedamage = DAMAGE_YES;
1280 self.event_damage = fd_secret_use;
1282 self.oldorigin = self.origin;
1284 self.wait = 5; // 5 seconds before closing