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;
153 if (self.sounds == 0)
156 if(self.spawnflags & 4)
159 if(self.dmg && (!self.message))
160 self.message = "was in the wrong place.";
162 if (self.sounds == 1)
164 precache_sound ("plats/plat1.wav");
165 precache_sound ("plats/plat2.wav");
166 self.noise = "plats/plat1.wav";
167 self.noise1 = "plats/plat2.wav";
170 if (self.sounds == 2)
172 precache_sound ("plats/medplat1.wav");
173 precache_sound ("plats/medplat2.wav");
174 self.noise = "plats/medplat1.wav";
175 self.noise1 = "plats/medplat2.wav";
180 precache_sound (self.sound1);
181 self.noise = self.sound1;
185 precache_sound (self.sound2);
186 self.noise1 = self.sound2;
189 self.mangle = self.angles;
190 self.angles = '0 0 0';
192 self.classname = "plat";
193 self.solid = SOLID_BSP;
194 self.movetype = MOVETYPE_PUSH;
195 setorigin (self, self.origin);
196 setmodel (self, self.model); // precision set below
197 self.effects |= EF_LOWPRECISION;
198 setsize (self, self.mins , self.maxs);
200 self.blocked = plat_crush;
205 self.pos1 = self.origin;
206 self.pos2 = self.origin;
207 self.pos2_z = self.origin_z - self.size_z + 8;
209 self.use = plat_trigger_use;
211 plat_spawn_inside_trigger (); // the "start moving" trigger
220 setorigin (self, self.pos2);
228 void() func_train_find;
230 void() train_blocked =
232 if (time < self.attack_finished)
234 self.attack_finished = time + 0.5;
238 if (self.think != func_train_find)
247 self.nextthink = self.ltime + self.wait;
248 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
251 self.nextthink = self.ltime + 0.1;
253 self.think = train_next;
260 targ = find (world, targetname, self.target);
261 self.target = targ.target;
263 objerror ("train_next: no next target");
265 self.wait = targ.wait;
268 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
269 SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);
272 void() func_train_find =
276 targ = find (world, targetname, self.target);
277 self.target = targ.target;
278 setorigin (self, targ.origin - self.mins);
279 if (!self.targetname)
280 { // not triggered, so start immediately
281 self.nextthink = self.ltime + 0.1;
282 self.think = train_next;
292 objerror ("func_train without a target");
294 if (self.sounds == 0)
296 self.noise = ("misc/null.wav");
297 precache_sound ("misc/null.wav");
298 self.noise1 = ("misc/null.wav");
299 precache_sound ("misc/null.wav");
302 if (self.sounds == 1)
304 self.noise = ("plats/train2.wav");
305 precache_sound ("plats/train2.wav");
306 self.noise1 = ("plats/train1.wav");
307 precache_sound ("plats/train1.wav");
310 self.solid = SOLID_BSP;
311 self.movetype = MOVETYPE_PUSH;
312 self.blocked = train_blocked;
313 self.use = train_use;
314 self.classname = "train";
316 setmodel (self, self.model); // precision set below
317 self.effects |= EF_LOWPRECISION;
318 setsize (self, self.mins , self.maxs);
319 setorigin (self, self.origin);
320 self.nextthink = self.ltime + 0.1;
321 self.think = func_train_find;
328 self.think = train_next;
329 self.nextthink = self.ltime + self.wait;
335 targ = find(world, targetname, self.target);
336 self.target = targ.target;
338 objerror("train_next: no next target");
339 self.wait = targ.wait;
343 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
345 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
348 void() func_train_find =
351 targ = find(world, targetname, self.target);
352 self.target = targ.target;
353 setorigin(self, targ.origin - self.mins);
354 self.nextthink = self.ltime + 1;
355 self.think = train_next;
358 /*QUAKED func_train (0 .5 .8) ?
359 Ridable platform, targets path_corner path to follow.
360 speed : speed the train moves (can be overridden by each path_corner)
361 target : targetname of first path_corner (starts here)
366 objerror("func_train without a target");
370 self.solid = SOLID_BSP;
371 self.movetype = MOVETYPE_PUSH;
373 setmodel(self, self.model); // precision set below
374 self.effects |= EF_LOWPRECISION;
375 setsize(self, self.mins, self.maxs);
376 setorigin(self, self.origin);
378 // wait for targets to spawn
379 self.nextthink = self.ltime + 0.1;
380 self.think = func_train_find;
385 void() rotating_blocked =
388 if(self.dmg && other.takedamage != DAMAGE_NO) {
389 if(self.dmgtime2 < time) {
390 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
391 self.dmgtime2 = time + self.dmgtime;
394 // Gib dead/dying stuff
395 if(other.deadflag != DEAD_NO)
396 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
402 /*QUAKED func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
403 Brush model that spins in place on one axis (default Z).
404 speed : speed to rotate (in degrees per second)
405 noise : path/name of looping .wav file to play.
406 dmg : Do this mutch dmg every .dmgtime intervall when blocked
410 void() func_rotating =
414 precache_sound(self.noise);
415 ambientsound(self.origin, self.noise, 1, ATTN_IDLE);
419 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
420 if (self.spawnflags & 4) // X (untested)
421 self.avelocity = '0 0 1' * self.speed;
422 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
423 else if (self.spawnflags & 8) // Y (untested)
424 self.avelocity = '1 0 0' * self.speed;
425 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
427 self.avelocity = '0 1 0' * self.speed;
429 if(self.dmg & (!self.message))
430 self.message = " was in the wrong place.";
433 if(self.dmg && (!self.dmgtime))
436 self.dmgtime2 = time;
439 self.solid = SOLID_BSP;
440 self.movetype = MOVETYPE_PUSH;
442 setmodel(self, self.model); // no lowprecision here! evil!
443 setsize(self, self.mins, self.maxs);
444 setorigin(self, self.origin);
446 self.blocked = rotating_blocked;
448 // wait for targets to spawn
449 self.nextthink = self.ltime + 999999999;
450 self.think = SUB_Null;
455 void() func_bobbing_controller_think =
458 self.nextthink = time + 0.1;
459 // calculate sinewave using makevectors
460 makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0');
461 v = self.owner.destvec + self.owner.movedir * v_forward_y;
462 // * 10 so it will arrive in 0.1 sec
463 self.owner.velocity = (v - self.owner.origin) * 10;
466 void() bobbing_blocked =
468 // no need to duplicate code
472 /*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
473 Brush model that moves back and forth on one axis (default Z).
474 speed : how long one cycle takes in seconds (default 4)
475 height : how far the cycle moves (default 32)
476 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
477 noise : path/name of looping .wav file to play.
478 dmg : Do this mutch dmg every .dmgtime intervall when blocked
481 void() func_bobbing =
483 local entity controller;
486 precache_sound(self.noise);
487 ambientsound(self.origin, self.noise, 1, ATTN_IDLE);
493 // center of bobbing motion
494 self.destvec = self.origin;
495 // time scale to get degrees
496 self.cnt = 360 / self.speed;
498 // damage when blocked
499 self.blocked = bobbing_blocked;
500 if(self.dmg & (!self.message))
501 self.message = " was in the wrong place.";
502 if(self.dmg && (!self.dmgtime))
504 self.dmgtime2 = time;
507 if (self.spawnflags & 1) // X
508 self.movedir = '1 0 0' * self.height;
509 else if (self.spawnflags & 2) // Y
510 self.movedir = '0 1 0' * self.height;
512 self.movedir = '0 0 1' * self.height;
514 self.solid = SOLID_BSP;
515 self.movetype = MOVETYPE_PUSH;
517 setmodel(self, self.model); // precision set below
518 setsize(self, self.mins, self.maxs);
519 setorigin(self, self.origin);
521 // wait for targets to spawn
522 controller = spawn();
523 controller.classname = "func_bobbing_controller";
524 controller.owner = self;
525 controller.nextthink = time + 1;
526 controller.think = func_bobbing_controller_think;
527 self.nextthink = self.ltime + 999999999;
528 self.think = SUB_Null;
530 // Savage: Reduce bandwith, critical on e.g. nexdm02
531 self.effects |= EF_LOWPRECISION;
534 // button and multiple button
537 void() button_return;
541 self.state = STATE_TOP;
542 self.nextthink = self.ltime + self.wait;
543 self.think = button_return;
544 activator = self.enemy;
546 self.frame = 1; // use alternate textures
551 self.state = STATE_BOTTOM;
554 void() button_return =
556 self.state = STATE_DOWN;
557 SUB_CalcMove (self.pos1, self.speed, button_done);
558 self.frame = 0; // use normal textures
560 self.takedamage = DAMAGE_YES; // can be shot again
564 void() button_blocked =
566 // do nothing, just don't come all the way back out
572 self.health = self.max_health;
573 self.takedamage = DAMAGE_NO; // will be reset upon return
575 if (self.state == STATE_UP || self.state == STATE_TOP)
578 if (self.noise != "")
579 sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
581 self.state = STATE_UP;
582 SUB_CalcMove (self.pos2, self.speed, button_wait);
588 // if (activator.classname != "player")
590 // dprint(activator.classname);
591 // dprint(" triggered a button\n");
593 self.enemy = activator;
597 void() button_touch =
599 // if (activator.classname != "player")
601 // dprint(activator.classname);
602 // dprint(" touched a button\n");
606 if (other.classname != "player")
610 self.enemy = other.owner;
614 void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) button_damage =
616 self.health = self.health - damage;
617 if (self.health <= 0)
619 // if (activator.classname != "player")
621 // dprint(activator.classname);
622 // dprint(" killed a button\n");
624 self.enemy = damage_attacker;
630 /*QUAKED func_button (0 .5 .8) ?
631 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.
633 "angle" determines the opening direction
634 "target" all entities with a matching targetname will be used
635 "speed" override the default 40 speed
636 "wait" override the default 1 second wait (-1 = never return)
637 "lip" override the default 4 pixel lip remaining at end of move
638 "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
649 self.movetype = MOVETYPE_PUSH;
650 self.solid = SOLID_BSP;
651 setmodel (self, self.model); // precision set below
652 self.effects |= EF_LOWPRECISION;
654 self.blocked = button_blocked;
655 self.use = button_use;
657 // if (self.health == 0) // all buttons are now shootable
661 self.max_health = self.health;
662 self.event_damage = button_damage;
663 self.takedamage = DAMAGE_YES;
666 self.touch = button_touch;
675 self.state = STATE_BOTTOM;
677 self.pos1 = self.origin;
678 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
682 float DOOR_START_OPEN = 1;
683 float DOOR_DONT_LINK = 4;
684 float DOOR_TOGGLE = 32;
688 Doors are similar to buttons, but can spawn a fat trigger field around them
689 to open without a touch, and they link together to form simultanious
692 Door.owner is the master door. If there is only one door, it points to itself.
693 If multiple doors, all will point to a single one.
695 Door.enemy chains from the master door through all doors linked in the chain.
700 =============================================================================
704 =============================================================================
710 void() door_blocked =
713 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
714 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
717 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
718 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
720 //Dont chamge direction for dead or dying stuff
721 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
724 if (self.state == STATE_DOWN)
730 //gib dying stuff just to make sure
731 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
732 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
736 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
737 // if a door has a negative wait, it would never come back if blocked,
738 // so let it just squash the object to death real fast
739 /* if (self.wait >= 0)
741 if (self.state == STATE_DOWN)
750 void() door_hit_top =
752 if (self.noise1 != "")
753 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
754 self.state = STATE_TOP;
755 if (self.spawnflags & DOOR_TOGGLE)
756 return; // don't come down automatically
757 self.think = door_go_down;
758 self.nextthink = self.ltime + self.wait;
761 void() door_hit_bottom =
763 if (self.noise1 != "")
764 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
765 self.state = STATE_BOTTOM;
768 void() door_go_down =
770 if (self.noise2 != "")
771 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
774 self.takedamage = DAMAGE_YES;
775 self.health = self.max_health;
778 self.state = STATE_DOWN;
779 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
784 if (self.state == STATE_UP)
785 return; // allready going up
787 if (self.state == STATE_TOP)
788 { // reset top wait time
789 self.nextthink = self.ltime + self.wait;
793 if (self.noise2 != "")
794 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
795 self.state = STATE_UP;
796 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
803 =============================================================================
807 =============================================================================
815 if (self.owner != self)
816 objerror ("door_fire: self.owner != self");
818 self.message = ""; // no more message
821 if (self.spawnflags & DOOR_TOGGLE)
823 if (self.state == STATE_UP || self.state == STATE_TOP)
830 } while ( (self != starte) && (self != world) );
836 // trigger all paired doors
842 } while ( (self != starte) && (self != world) );
851 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
852 self.message = ""; // door message are for touch only
854 self.owner.message = "";
856 self.enemy.message = "";
867 void() door_trigger_touch =
869 if (other.health < 1)
872 if (time < self.attack_finished)
874 self.attack_finished = time + 1;
883 void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) door_damage =
886 self.health = self.health - damage;
887 if (self.health <= 0)
891 self.health = self.max_health;
892 self.takedamage = DAMAGE_NO; // wil be reset upon return
908 if (other.classname != "player")
910 if (self.owner.attack_finished > time)
913 self.owner.attack_finished = time + 2;
915 if (self.owner.message != "")
917 if (other.flags & FL_CLIENT)
918 centerprint (other, self.owner.message);
919 sound (other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
924 =============================================================================
928 =============================================================================
932 entity(vector fmins, vector fmaxs) spawn_field =
934 local entity trigger;
938 trigger.classname = "doortriggerfield";
939 trigger.movetype = MOVETYPE_NONE;
940 trigger.solid = SOLID_TRIGGER;
941 trigger.owner = self;
942 trigger.touch = door_trigger_touch;
946 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
951 float (entity e1, entity e2) EntitiesTouching =
953 if (e1.mins_x > e2.maxs_x)
955 if (e1.mins_y > e2.maxs_y)
957 if (e1.mins_z > e2.maxs_z)
959 if (e1.maxs_x < e2.mins_x)
961 if (e1.maxs_y < e2.mins_y)
963 if (e1.maxs_z < e2.mins_z)
978 local entity t, starte;
979 local vector cmins, cmaxs;
982 return; // already linked by another door
983 if (self.spawnflags & 4)
985 self.owner = self.enemy = self;
986 return; // don't want to link this door
997 self.owner = starte; // master door
1000 starte.health = self.health;
1001 if (self.targetname)
1002 starte.targetname = self.targetname;
1003 if (self.message != "")
1004 starte.message = self.message;
1006 t = find(t, classname, self.classname);
1009 self.enemy = starte; // make the chain a loop
1011 // shootable, or triggered doors just needed the owner/enemy links,
1012 // they don't spawn a field
1018 if (self.targetname)
1023 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1028 if (EntitiesTouching(self,t))
1031 objerror ("cross connected doors");
1036 if (t.mins_x < cmins_x)
1038 if (t.mins_y < cmins_y)
1040 if (t.mins_z < cmins_z)
1042 if (t.maxs_x > cmaxs_x)
1044 if (t.maxs_y > cmaxs_y)
1046 if (t.maxs_z > cmaxs_z)
1054 /*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1055 if two doors touch, they are assumed to be connected and operate as a unit.
1057 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1059 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).
1061 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1062 "angle" determines the opening direction
1063 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1064 "health" if set, door must be shot open
1065 "speed" movement speed (100 default)
1066 "wait" wait before returning (3 default, -1 = never return)
1067 "lip" lip remaining at end of move (8 default)
1068 "dmg" damage to inflict when blocked (2 default)
1075 FIXME: only one sound set available at the time being
1081 //if (!self.deathtype) // map makers can override this
1082 // self.deathtype = " got in the way";
1085 self.max_health = self.health;
1086 self.solid = SOLID_BSP;
1087 self.movetype = MOVETYPE_PUSH;
1088 setorigin (self, self.origin);
1089 setmodel (self, self.model); // precision set below
1090 self.effects |= EF_LOWPRECISION;
1091 self.classname = "door";
1093 self.blocked = door_blocked;
1094 self.use = door_use;
1096 if(self.targetname == "") {
1097 self.spawnflags = 0;
1101 if(self.spawnflags & 4)
1104 if(self.dmg & (!self.message))
1105 self.message = "was in the wrong place.";
1109 if (self.sounds > 0)
1111 precache_sound ("plats/medplat1.wav");
1112 precache_sound ("plats/medplat2.wav");
1113 self.noise2 = "plats/medplat1.wav";
1114 self.noise1 = "plats/medplat2.wav";
1126 self.pos1 = self.origin;
1127 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1129 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1130 // but spawn in the open position
1131 if (self.spawnflags & DOOR_START_OPEN)
1133 setorigin (self, self.pos2);
1134 self.pos2 = self.pos1;
1135 self.pos1 = self.origin;
1138 self.state = STATE_BOTTOM;
1142 self.takedamage = DAMAGE_YES;
1143 self.event_damage = door_damage;
1149 self.touch = door_touch;
1151 // LinkDoors can't be done until all of the doors have been spawned, so
1152 // the sizes can be detected properly.
1153 self.think = LinkDoors;
1154 self.nextthink = self.ltime + 0.1;
1158 =============================================================================
1162 =============================================================================
1165 void() fd_secret_move1;
1166 void() fd_secret_move2;
1167 void() fd_secret_move3;
1168 void() fd_secret_move4;
1169 void() fd_secret_move5;
1170 void() fd_secret_move6;
1171 void() fd_secret_done;
1173 float SECRET_OPEN_ONCE = 1; // stays open
1174 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1175 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1176 float SECRET_NO_SHOOT = 8; // only opened by trigger
1177 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1180 void () fd_secret_use =
1184 self.health = 10000;
1185 self.bot_attack = TRUE;
1187 // exit if still moving around...
1188 if (self.origin != self.oldorigin)
1191 self.message = ""; // no more message
1193 SUB_UseTargets(); // fire all targets / killtargets
1195 self.velocity = '0 0 0';
1197 // Make a sound, wait a little...
1199 if (self.noise1 != "")
1200 sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
1201 self.nextthink = self.ltime + 0.1;
1203 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1204 makevectors(self.mangle);
1208 if (self.spawnflags & SECRET_1ST_DOWN)
1209 self.t_width = fabs(v_up * self.size);
1211 self.t_width = fabs(v_right * self.size);
1215 self.t_length = fabs(v_forward * self.size);
1217 if (self.spawnflags & SECRET_1ST_DOWN)
1218 self.dest1 = self.origin - v_up * self.t_width;
1220 self.dest1 = self.origin + v_right * (self.t_width * temp);
1222 self.dest2 = self.dest1 + v_forward * self.t_length;
1223 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1224 if (self.noise2 != "")
1225 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1228 // Wait after first movement...
1229 void () fd_secret_move1 =
1231 self.nextthink = self.ltime + 1.0;
1232 self.think = fd_secret_move2;
1233 if (self.noise3 != "")
1234 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1237 // Start moving sideways w/sound...
1238 void () fd_secret_move2 =
1240 if (self.noise2 != "")
1241 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1242 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1245 // Wait here until time to go back...
1246 void () fd_secret_move3 =
1248 if (self.noise3 != "")
1249 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1250 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1252 self.nextthink = self.ltime + self.wait;
1253 self.think = fd_secret_move4;
1258 void () fd_secret_move4 =
1260 if (self.noise2 != "")
1261 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1262 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1266 void () fd_secret_move5 =
1268 self.nextthink = self.ltime + 1.0;
1269 self.think = fd_secret_move6;
1270 if (self.noise3 != "")
1271 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1274 void () fd_secret_move6 =
1276 if (self.noise2 != "")
1277 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1278 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1281 void () fd_secret_done =
1283 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1285 self.health = 10000;
1286 self.takedamage = DAMAGE_YES;
1287 //self.th_pain = fd_secret_use;
1289 if (self.noise3 != "")
1290 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1293 void () secret_blocked =
1295 if (time < self.attack_finished)
1297 self.attack_finished = time + 0.5;
1298 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1308 void() secret_touch =
1310 if (activator.classname != "player")
1312 if (self.attack_finished > time)
1315 self.attack_finished = time + 2;
1319 if (other.flags & FL_CLIENT)
1320 centerprint (other, self.message);
1321 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
1326 /*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1327 Basic secret door. Slides back, then to the side. Angle determines direction.
1328 wait = # of seconds before coming back
1329 1st_left = 1st move is left of arrow
1330 1st_down = 1st move is down from arrow
1331 always_shoot = even if targeted, keep shootable
1332 t_width = override WIDTH to move back (or height if going down)
1333 t_length = override LENGTH to move sideways
1334 "dmg" damage to inflict when blocked (2 default)
1336 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1343 void () func_door_secret =
1345 /*if (!self.deathtype) // map makers can override this
1346 self.deathtype = " got in the way";*/
1352 self.mangle = self.angles;
1353 self.angles = '0 0 0';
1354 self.solid = SOLID_BSP;
1355 self.movetype = MOVETYPE_PUSH;
1356 self.classname = "door";
1357 setmodel (self, self.model); // precision set below
1358 self.effects |= EF_LOWPRECISION;
1359 setorigin (self, self.origin);
1361 self.touch = secret_touch;
1362 self.blocked = secret_blocked;
1364 self.use = fd_secret_use;
1365 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1367 self.health = 10000;
1368 self.takedamage = DAMAGE_YES;
1369 self.event_damage = fd_secret_use;
1371 self.oldorigin = self.origin;
1373 self.wait = 5; // 5 seconds before closing