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_VOICE, self.noise1, 1, ATTN_NORM);
54 self.think = plat_go_down;
55 self.nextthink = self.ltime + 3;
58 void plat_hit_bottom()
60 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
66 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
68 SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
73 sound (self, CHAN_VOICE, self.noise, 1, 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 self.solid = SOLID_BSP;
195 self.movetype = MOVETYPE_PUSH;
196 setorigin (self, self.origin);
197 setmodel (self, self.model); // precision set below
198 self.effects |= EF_LOWPRECISION;
199 setsize (self, self.mins , self.maxs);
201 self.blocked = plat_crush;
206 self.pos1 = self.origin;
207 self.pos2 = self.origin;
208 self.pos2_z = self.origin_z - self.size_z + 8;
210 self.use = plat_trigger_use;
212 plat_spawn_inside_trigger (); // the "start moving" trigger
221 setorigin (self, self.pos2);
229 void() func_train_find;
233 if (time < self.attack_finished_single)
235 self.attack_finished_single = time + 0.5;
239 if (self.think != func_train_find)
248 self.nextthink = self.ltime + self.wait;
249 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
252 self.nextthink = self.ltime + 0.1;
254 self.think = train_next;
261 targ = find (world, targetname, self.target);
262 self.target = targ.target;
264 objerror ("train_next: no next target");
266 self.wait = targ.wait;
269 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
270 SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);
273 void func_train_find()
277 targ = find (world, targetname, self.target);
278 self.target = targ.target;
279 setorigin (self, targ.origin - self.mins);
280 if (!self.targetname)
281 { // not triggered, so start immediately
282 self.nextthink = self.ltime + 0.1;
283 self.think = train_next;
288 void spawnfunc_func_train()
293 objerror ("func_train without a target");
295 if (self.sounds == 0)
297 self.noise = ("misc/null.wav");
298 precache_sound ("misc/null.wav");
299 self.noise1 = ("misc/null.wav");
300 precache_sound ("misc/null.wav");
303 if (self.sounds == 1)
305 self.noise = ("plats/train2.wav");
306 precache_sound ("plats/train2.wav");
307 self.noise1 = ("plats/train1.wav");
308 precache_sound ("plats/train1.wav");
311 self.solid = SOLID_BSP;
312 self.movetype = MOVETYPE_PUSH;
313 self.blocked = train_blocked;
314 self.use = train_use;
315 self.classname = "train";
317 setmodel (self, self.model); // precision set below
318 self.effects |= EF_LOWPRECISION;
319 setsize (self, self.mins , self.maxs);
320 setorigin (self, self.origin);
321 self.nextthink = self.ltime + 0.1;
322 self.think = func_train_find;
329 self.think = train_next;
330 self.nextthink = self.ltime + self.wait;
336 targ = find(world, targetname, self.target);
337 self.target = targ.target;
339 objerror("train_next: no next target");
340 self.wait = targ.wait;
344 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
346 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
349 void func_train_find()
352 targ = find(world, targetname, self.target);
353 self.target = targ.target;
354 setorigin(self, targ.origin - self.mins);
355 self.nextthink = self.ltime + 1;
356 self.think = train_next;
359 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
360 Ridable platform, targets spawnfunc_path_corner path to follow.
361 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
362 target : targetname of first spawnfunc_path_corner (starts here)
364 void spawnfunc_func_train()
367 objerror("func_train without a target");
371 self.solid = SOLID_BSP;
372 self.movetype = MOVETYPE_PUSH;
374 setmodel(self, self.model); // precision set below
375 self.effects |= EF_LOWPRECISION;
376 setsize(self, self.mins, self.maxs);
377 setorigin(self, self.origin);
379 // wait for targets to spawn
380 self.nextthink = self.ltime + 0.1;
381 self.think = func_train_find;
386 void rotating_blocked()
389 if(self.dmg && other.takedamage != DAMAGE_NO) {
390 if(self.dmgtime2 < time) {
391 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
392 self.dmgtime2 = time + self.dmgtime;
395 // Gib dead/dying stuff
396 if(other.deadflag != DEAD_NO)
397 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
403 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
404 Brush model that spins in place on one axis (default Z).
405 speed : speed to rotate (in degrees per second)
406 noise : path/name of looping .wav file to play.
407 dmg : Do this mutch dmg every .dmgtime intervall when blocked
411 void spawnfunc_func_rotating()
415 precache_sound(self.noise);
416 ambientsound(self.origin, self.noise, 1, ATTN_IDLE);
420 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
421 if (self.spawnflags & 4) // X (untested)
422 self.avelocity = '0 0 1' * self.speed;
423 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
424 else if (self.spawnflags & 8) // Y (untested)
425 self.avelocity = '1 0 0' * self.speed;
426 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
428 self.avelocity = '0 1 0' * self.speed;
430 if(self.dmg & (!self.message))
431 self.message = " was in the wrong place.";
434 if(self.dmg && (!self.dmgtime))
437 self.dmgtime2 = time;
440 self.solid = SOLID_BSP;
441 self.movetype = MOVETYPE_PUSH;
443 setmodel(self, self.model); // no lowprecision here! evil!
444 setsize(self, self.mins, self.maxs);
445 setorigin(self, self.origin);
447 self.blocked = rotating_blocked;
449 // wait for targets to spawn
450 self.nextthink = self.ltime + 999999999;
451 self.think = SUB_Null;
456 void func_bobbing_controller_think()
459 self.nextthink = time + 0.1;
460 // calculate sinewave using makevectors
461 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
462 v = self.owner.destvec + self.owner.movedir * v_forward_y;
463 // * 10 so it will arrive in 0.1 sec
464 self.owner.velocity = (v - self.owner.origin) * 10;
467 void bobbing_blocked()
469 // no need to duplicate code
473 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
474 Brush model that moves back and forth on one axis (default Z).
475 speed : how long one cycle takes in seconds (default 4)
476 height : how far the cycle moves (default 32)
477 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
478 noise : path/name of looping .wav file to play.
479 dmg : Do this mutch dmg every .dmgtime intervall when blocked
482 void spawnfunc_func_bobbing()
484 local entity controller;
487 precache_sound(self.noise);
488 ambientsound(self.origin, self.noise, 1, ATTN_IDLE);
494 // center of bobbing motion
495 self.destvec = self.origin;
496 // time scale to get degrees
497 self.cnt = 360 / self.speed;
499 // damage when blocked
500 self.blocked = bobbing_blocked;
501 if(self.dmg & (!self.message))
502 self.message = " was in the wrong place.";
503 if(self.dmg && (!self.dmgtime))
505 self.dmgtime2 = time;
508 if (self.spawnflags & 1) // X
509 self.movedir = '1 0 0' * self.height;
510 else if (self.spawnflags & 2) // Y
511 self.movedir = '0 1 0' * self.height;
513 self.movedir = '0 0 1' * self.height;
515 self.solid = SOLID_BSP;
516 self.movetype = MOVETYPE_PUSH;
518 setmodel(self, self.model); // precision set below
519 setsize(self, self.mins, self.maxs);
520 setorigin(self, self.origin);
522 // wait for targets to spawn
523 controller = spawn();
524 controller.classname = "func_bobbing_controller";
525 controller.owner = self;
526 controller.nextthink = time + 1;
527 controller.think = func_bobbing_controller_think;
528 self.nextthink = self.ltime + 999999999;
529 self.think = SUB_Null;
531 // Savage: Reduce bandwith, critical on e.g. nexdm02
532 self.effects |= EF_LOWPRECISION;
535 // button and multiple button
538 void() button_return;
542 self.state = STATE_TOP;
543 self.nextthink = self.ltime + self.wait;
544 self.think = button_return;
545 activator = self.enemy;
547 self.frame = 1; // use alternate textures
552 self.state = STATE_BOTTOM;
557 self.state = STATE_DOWN;
558 SUB_CalcMove (self.pos1, self.speed, button_done);
559 self.frame = 0; // use normal textures
561 self.takedamage = DAMAGE_YES; // can be shot again
565 void button_blocked()
567 // do nothing, just don't come all the way back out
573 self.health = self.max_health;
574 self.takedamage = DAMAGE_NO; // will be reset upon return
576 if (self.state == STATE_UP || self.state == STATE_TOP)
579 if (self.noise != "")
580 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
582 self.state = STATE_UP;
583 SUB_CalcMove (self.pos2, self.speed, button_wait);
589 // if (activator.classname != "player")
591 // dprint(activator.classname);
592 // dprint(" triggered a button\n");
594 self.enemy = activator;
600 // if (activator.classname != "player")
602 // dprint(activator.classname);
603 // dprint(" touched a button\n");
607 if (other.classname != "player")
611 self.enemy = other.owner;
615 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
617 self.health = self.health - damage;
618 if (self.health <= 0)
620 // if (activator.classname != "player")
622 // dprint(activator.classname);
623 // dprint(" killed a button\n");
625 self.enemy = damage_attacker;
631 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
632 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.
634 "angle" determines the opening direction
635 "target" all entities with a matching targetname will be used
636 "speed" override the default 40 speed
637 "wait" override the default 1 second wait (-1 = never return)
638 "lip" override the default 4 pixel lip remaining at end of move
639 "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
646 void spawnfunc_func_button()
650 self.movetype = MOVETYPE_PUSH;
651 self.solid = SOLID_BSP;
652 setmodel (self, self.model); // precision set below
653 self.effects |= EF_LOWPRECISION;
655 self.blocked = button_blocked;
656 self.use = button_use;
658 // if (self.health == 0) // all buttons are now shootable
662 self.max_health = self.health;
663 self.event_damage = button_damage;
664 self.takedamage = DAMAGE_YES;
667 self.touch = button_touch;
677 precache_sound(self.noise);
679 self.state = STATE_BOTTOM;
681 self.pos1 = self.origin;
682 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
686 float DOOR_START_OPEN = 1;
687 float DOOR_DONT_LINK = 4;
688 float DOOR_TOGGLE = 32;
692 Doors are similar to buttons, but can spawn a fat trigger field around them
693 to open without a touch, and they link together to form simultanious
696 Door.owner is the master door. If there is only one door, it points to itself.
697 If multiple doors, all will point to a single one.
699 Door.enemy chains from the master door through all doors linked in the chain.
704 =============================================================================
708 =============================================================================
717 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
718 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
721 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
722 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
724 //Dont chamge direction for dead or dying stuff
725 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
728 if (self.state == STATE_DOWN)
734 //gib dying stuff just to make sure
735 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
736 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
740 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
741 // if a door has a negative wait, it would never come back if blocked,
742 // so let it just squash the object to death real fast
743 /* if (self.wait >= 0)
745 if (self.state == STATE_DOWN)
756 if (self.noise1 != "")
757 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
758 self.state = STATE_TOP;
759 if (self.spawnflags & DOOR_TOGGLE)
760 return; // don't come down automatically
761 self.think = door_go_down;
762 self.nextthink = self.ltime + self.wait;
765 void door_hit_bottom()
767 if (self.noise1 != "")
768 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
769 self.state = STATE_BOTTOM;
774 if (self.noise2 != "")
775 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
778 self.takedamage = DAMAGE_YES;
779 self.health = self.max_health;
782 self.state = STATE_DOWN;
783 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
788 if (self.state == STATE_UP)
789 return; // allready going up
791 if (self.state == STATE_TOP)
792 { // reset top wait time
793 self.nextthink = self.ltime + self.wait;
797 if (self.noise2 != "")
798 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
799 self.state = STATE_UP;
800 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
807 =============================================================================
811 =============================================================================
819 if (self.owner != self)
820 objerror ("door_fire: self.owner != self");
822 self.message = ""; // no more message
825 if (self.spawnflags & DOOR_TOGGLE)
827 if (self.state == STATE_UP || self.state == STATE_TOP)
834 } while ( (self != starte) && (self != world) );
840 // trigger all paired doors
846 } while ( (self != starte) && (self != world) );
855 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
856 self.message = ""; // door message are for touch only
858 self.owner.message = "";
860 self.enemy.message = "";
871 void door_trigger_touch()
873 if (other.health < 1)
876 if (time < self.attack_finished_single)
878 self.attack_finished_single = time + 1;
887 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
890 self.health = self.health - damage;
891 if (self.health <= 0)
895 self.health = self.max_health;
896 self.takedamage = DAMAGE_NO; // wil be reset upon return
912 if (other.classname != "player")
914 if (self.owner.attack_finished_single > time)
917 self.owner.attack_finished_single = time + 2;
919 if (self.owner.message != "")
921 if (other.flags & FL_CLIENT)
922 centerprint (other, self.owner.message);
923 sound (other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
928 =============================================================================
932 =============================================================================
936 entity spawn_field(vector fmins, vector fmaxs)
938 local entity trigger;
942 trigger.classname = "doortriggerfield";
943 trigger.movetype = MOVETYPE_NONE;
944 trigger.solid = SOLID_TRIGGER;
945 trigger.owner = self;
946 trigger.touch = door_trigger_touch;
950 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
955 float EntitiesTouching(entity e1, entity e2)
957 if (e1.mins_x > e2.maxs_x)
959 if (e1.mins_y > e2.maxs_y)
961 if (e1.mins_z > e2.maxs_z)
963 if (e1.maxs_x < e2.mins_x)
965 if (e1.maxs_y < e2.mins_y)
967 if (e1.maxs_z < e2.mins_z)
982 local entity t, starte;
983 local vector cmins, cmaxs;
986 return; // already linked by another door
987 if (self.spawnflags & 4)
989 self.owner = self.enemy = self;
990 return; // don't want to link this door
1001 self.owner = starte; // master door
1004 starte.health = self.health;
1005 if (self.targetname)
1006 starte.targetname = self.targetname;
1007 if (self.message != "")
1008 starte.message = self.message;
1010 t = find(t, classname, self.classname);
1013 self.enemy = starte; // make the chain a loop
1015 // shootable, or triggered doors just needed the owner/enemy links,
1016 // they don't spawn a field
1022 if (self.targetname)
1027 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1032 if (EntitiesTouching(self,t))
1035 objerror ("cross connected doors");
1040 if (t.mins_x < cmins_x)
1042 if (t.mins_y < cmins_y)
1044 if (t.mins_z < cmins_z)
1046 if (t.maxs_x > cmaxs_x)
1048 if (t.maxs_y > cmaxs_y)
1050 if (t.maxs_z > cmaxs_z)
1058 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1059 if two doors touch, they are assumed to be connected and operate as a unit.
1061 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1063 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).
1065 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1066 "angle" determines the opening direction
1067 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1068 "health" if set, door must be shot open
1069 "speed" movement speed (100 default)
1070 "wait" wait before returning (3 default, -1 = never return)
1071 "lip" lip remaining at end of move (8 default)
1072 "dmg" damage to inflict when blocked (2 default)
1079 FIXME: only one sound set available at the time being
1083 void spawnfunc_func_door()
1085 //if (!self.deathtype) // map makers can override this
1086 // self.deathtype = " got in the way";
1089 self.max_health = self.health;
1090 self.solid = SOLID_BSP;
1091 self.movetype = MOVETYPE_PUSH;
1092 setorigin (self, self.origin);
1093 setmodel (self, self.model); // precision set below
1094 self.effects |= EF_LOWPRECISION;
1095 self.classname = "door";
1097 self.blocked = door_blocked;
1098 self.use = door_use;
1100 if(self.targetname == "") {
1101 self.spawnflags = 0;
1105 if(self.spawnflags & 8)
1108 if(self.dmg & (!self.message))
1109 self.message = "was in the wrong place.";
1113 if (self.sounds > 0)
1115 precache_sound ("plats/medplat1.wav");
1116 precache_sound ("plats/medplat2.wav");
1117 self.noise2 = "plats/medplat1.wav";
1118 self.noise1 = "plats/medplat2.wav";
1130 self.pos1 = self.origin;
1131 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1133 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1134 // but spawn in the open position
1135 if (self.spawnflags & DOOR_START_OPEN)
1137 setorigin (self, self.pos2);
1138 self.pos2 = self.pos1;
1139 self.pos1 = self.origin;
1142 self.state = STATE_BOTTOM;
1146 self.takedamage = DAMAGE_YES;
1147 self.event_damage = door_damage;
1153 self.touch = door_touch;
1155 // LinkDoors can't be done until all of the doors have been spawned, so
1156 // the sizes can be detected properly.
1157 self.think = LinkDoors;
1158 self.nextthink = self.ltime + 0.1;
1162 =============================================================================
1166 =============================================================================
1169 void() fd_secret_move1;
1170 void() fd_secret_move2;
1171 void() fd_secret_move3;
1172 void() fd_secret_move4;
1173 void() fd_secret_move5;
1174 void() fd_secret_move6;
1175 void() fd_secret_done;
1177 float SECRET_OPEN_ONCE = 1; // stays open
1178 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1179 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1180 float SECRET_NO_SHOOT = 8; // only opened by trigger
1181 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1184 void fd_secret_use()
1188 self.health = 10000;
1189 self.bot_attack = TRUE;
1191 // exit if still moving around...
1192 if (self.origin != self.oldorigin)
1195 self.message = ""; // no more message
1197 SUB_UseTargets(); // fire all targets / killtargets
1199 self.velocity = '0 0 0';
1201 // Make a sound, wait a little...
1203 if (self.noise1 != "")
1204 sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
1205 self.nextthink = self.ltime + 0.1;
1207 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1208 makevectors(self.mangle);
1212 if (self.spawnflags & SECRET_1ST_DOWN)
1213 self.t_width = fabs(v_up * self.size);
1215 self.t_width = fabs(v_right * self.size);
1219 self.t_length = fabs(v_forward * self.size);
1221 if (self.spawnflags & SECRET_1ST_DOWN)
1222 self.dest1 = self.origin - v_up * self.t_width;
1224 self.dest1 = self.origin + v_right * (self.t_width * temp);
1226 self.dest2 = self.dest1 + v_forward * self.t_length;
1227 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1228 if (self.noise2 != "")
1229 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1232 // Wait after first movement...
1233 void fd_secret_move1()
1235 self.nextthink = self.ltime + 1.0;
1236 self.think = fd_secret_move2;
1237 if (self.noise3 != "")
1238 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1241 // Start moving sideways w/sound...
1242 void fd_secret_move2()
1244 if (self.noise2 != "")
1245 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1246 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1249 // Wait here until time to go back...
1250 void fd_secret_move3()
1252 if (self.noise3 != "")
1253 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1254 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1256 self.nextthink = self.ltime + self.wait;
1257 self.think = fd_secret_move4;
1262 void fd_secret_move4()
1264 if (self.noise2 != "")
1265 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1266 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1270 void fd_secret_move5()
1272 self.nextthink = self.ltime + 1.0;
1273 self.think = fd_secret_move6;
1274 if (self.noise3 != "")
1275 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1278 void fd_secret_move6()
1280 if (self.noise2 != "")
1281 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1282 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1285 void fd_secret_done()
1287 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1289 self.health = 10000;
1290 self.takedamage = DAMAGE_YES;
1291 //self.th_pain = fd_secret_use;
1293 if (self.noise3 != "")
1294 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1297 void secret_blocked()
1299 if (time < self.attack_finished_single)
1301 self.attack_finished_single = time + 0.5;
1302 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1314 if (activator.classname != "player")
1316 if (self.attack_finished_single > time)
1319 self.attack_finished_single = time + 2;
1323 if (other.flags & FL_CLIENT)
1324 centerprint (other, self.message);
1325 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
1330 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1331 Basic secret door. Slides back, then to the side. Angle determines direction.
1332 wait = # of seconds before coming back
1333 1st_left = 1st move is left of arrow
1334 1st_down = 1st move is down from arrow
1335 always_shoot = even if targeted, keep shootable
1336 t_width = override WIDTH to move back (or height if going down)
1337 t_length = override LENGTH to move sideways
1338 "dmg" damage to inflict when blocked (2 default)
1340 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1347 void spawnfunc_func_door_secret()
1349 /*if (!self.deathtype) // map makers can override this
1350 self.deathtype = " got in the way";*/
1356 self.mangle = self.angles;
1357 self.angles = '0 0 0';
1358 self.solid = SOLID_BSP;
1359 self.movetype = MOVETYPE_PUSH;
1360 self.classname = "door";
1361 setmodel (self, self.model); // precision set below
1362 self.effects |= EF_LOWPRECISION;
1363 setorigin (self, self.origin);
1365 self.touch = secret_touch;
1366 self.blocked = secret_blocked;
1368 self.use = fd_secret_use;
1369 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1371 self.health = 10000;
1372 self.takedamage = DAMAGE_YES;
1373 self.event_damage = fd_secret_use;
1375 self.oldorigin = self.origin;
1377 self.wait = 5; // 5 seconds before closing