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() path_corner = { };
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;
231 void() train_blocked =
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;
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 func_train (0 .5 .8) ?
360 Ridable platform, targets path_corner path to follow.
361 speed : speed the train moves (can be overridden by each path_corner)
362 target : targetname of first path_corner (starts here)
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 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() 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 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() 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;
555 void() button_return =
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;
598 void() button_touch =
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(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) button_damage =
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 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
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;
676 self.state = STATE_BOTTOM;
678 self.pos1 = self.origin;
679 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
683 float DOOR_START_OPEN = 1;
684 float DOOR_DONT_LINK = 4;
685 float DOOR_TOGGLE = 32;
689 Doors are similar to buttons, but can spawn a fat trigger field around them
690 to open without a touch, and they link together to form simultanious
693 Door.owner is the master door. If there is only one door, it points to itself.
694 If multiple doors, all will point to a single one.
696 Door.enemy chains from the master door through all doors linked in the chain.
701 =============================================================================
705 =============================================================================
711 void() door_blocked =
714 if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
715 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
718 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
719 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
721 //Dont chamge direction for dead or dying stuff
722 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
725 if (self.state == STATE_DOWN)
731 //gib dying stuff just to make sure
732 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
733 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
737 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
738 // if a door has a negative wait, it would never come back if blocked,
739 // so let it just squash the object to death real fast
740 /* if (self.wait >= 0)
742 if (self.state == STATE_DOWN)
751 void() door_hit_top =
753 if (self.noise1 != "")
754 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
755 self.state = STATE_TOP;
756 if (self.spawnflags & DOOR_TOGGLE)
757 return; // don't come down automatically
758 self.think = door_go_down;
759 self.nextthink = self.ltime + self.wait;
762 void() door_hit_bottom =
764 if (self.noise1 != "")
765 sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
766 self.state = STATE_BOTTOM;
769 void() door_go_down =
771 if (self.noise2 != "")
772 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
775 self.takedamage = DAMAGE_YES;
776 self.health = self.max_health;
779 self.state = STATE_DOWN;
780 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
785 if (self.state == STATE_UP)
786 return; // allready going up
788 if (self.state == STATE_TOP)
789 { // reset top wait time
790 self.nextthink = self.ltime + self.wait;
794 if (self.noise2 != "")
795 sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
796 self.state = STATE_UP;
797 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
804 =============================================================================
808 =============================================================================
816 if (self.owner != self)
817 objerror ("door_fire: self.owner != self");
819 self.message = ""; // no more message
822 if (self.spawnflags & DOOR_TOGGLE)
824 if (self.state == STATE_UP || self.state == STATE_TOP)
831 } while ( (self != starte) && (self != world) );
837 // trigger all paired doors
843 } while ( (self != starte) && (self != world) );
852 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
853 self.message = ""; // door message are for touch only
855 self.owner.message = "";
857 self.enemy.message = "";
868 void() door_trigger_touch =
870 if (other.health < 1)
873 if (time < self.attack_finished_single)
875 self.attack_finished_single = time + 1;
884 void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) door_damage =
887 self.health = self.health - damage;
888 if (self.health <= 0)
892 self.health = self.max_health;
893 self.takedamage = DAMAGE_NO; // wil be reset upon return
909 if (other.classname != "player")
911 if (self.owner.attack_finished_single > time)
914 self.owner.attack_finished_single = time + 2;
916 if (self.owner.message != "")
918 if (other.flags & FL_CLIENT)
919 centerprint (other, self.owner.message);
920 sound (other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
925 =============================================================================
929 =============================================================================
933 entity(vector fmins, vector fmaxs) spawn_field =
935 local entity trigger;
939 trigger.classname = "doortriggerfield";
940 trigger.movetype = MOVETYPE_NONE;
941 trigger.solid = SOLID_TRIGGER;
942 trigger.owner = self;
943 trigger.touch = door_trigger_touch;
947 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
952 float (entity e1, entity e2) EntitiesTouching =
954 if (e1.mins_x > e2.maxs_x)
956 if (e1.mins_y > e2.maxs_y)
958 if (e1.mins_z > e2.maxs_z)
960 if (e1.maxs_x < e2.mins_x)
962 if (e1.maxs_y < e2.mins_y)
964 if (e1.maxs_z < e2.mins_z)
979 local entity t, starte;
980 local vector cmins, cmaxs;
983 return; // already linked by another door
984 if (self.spawnflags & 4)
986 self.owner = self.enemy = self;
987 return; // don't want to link this door
998 self.owner = starte; // master door
1001 starte.health = self.health;
1002 if (self.targetname)
1003 starte.targetname = self.targetname;
1004 if (self.message != "")
1005 starte.message = self.message;
1007 t = find(t, classname, self.classname);
1010 self.enemy = starte; // make the chain a loop
1012 // shootable, or triggered doors just needed the owner/enemy links,
1013 // they don't spawn a field
1019 if (self.targetname)
1024 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1029 if (EntitiesTouching(self,t))
1032 objerror ("cross connected doors");
1037 if (t.mins_x < cmins_x)
1039 if (t.mins_y < cmins_y)
1041 if (t.mins_z < cmins_z)
1043 if (t.maxs_x > cmaxs_x)
1045 if (t.maxs_y > cmaxs_y)
1047 if (t.maxs_z > cmaxs_z)
1055 /*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1056 if two doors touch, they are assumed to be connected and operate as a unit.
1058 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1060 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).
1062 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1063 "angle" determines the opening direction
1064 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1065 "health" if set, door must be shot open
1066 "speed" movement speed (100 default)
1067 "wait" wait before returning (3 default, -1 = never return)
1068 "lip" lip remaining at end of move (8 default)
1069 "dmg" damage to inflict when blocked (2 default)
1076 FIXME: only one sound set available at the time being
1082 //if (!self.deathtype) // map makers can override this
1083 // self.deathtype = " got in the way";
1086 self.max_health = self.health;
1087 self.solid = SOLID_BSP;
1088 self.movetype = MOVETYPE_PUSH;
1089 setorigin (self, self.origin);
1090 setmodel (self, self.model); // precision set below
1091 self.effects |= EF_LOWPRECISION;
1092 self.classname = "door";
1094 self.blocked = door_blocked;
1095 self.use = door_use;
1097 if(self.targetname == "") {
1098 self.spawnflags = 0;
1102 if(self.spawnflags & 4)
1105 if(self.dmg & (!self.message))
1106 self.message = "was in the wrong place.";
1110 if (self.sounds > 0)
1112 precache_sound ("plats/medplat1.wav");
1113 precache_sound ("plats/medplat2.wav");
1114 self.noise2 = "plats/medplat1.wav";
1115 self.noise1 = "plats/medplat2.wav";
1127 self.pos1 = self.origin;
1128 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1130 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1131 // but spawn in the open position
1132 if (self.spawnflags & DOOR_START_OPEN)
1134 setorigin (self, self.pos2);
1135 self.pos2 = self.pos1;
1136 self.pos1 = self.origin;
1139 self.state = STATE_BOTTOM;
1143 self.takedamage = DAMAGE_YES;
1144 self.event_damage = door_damage;
1150 self.touch = door_touch;
1152 // LinkDoors can't be done until all of the doors have been spawned, so
1153 // the sizes can be detected properly.
1154 self.think = LinkDoors;
1155 self.nextthink = self.ltime + 0.1;
1159 =============================================================================
1163 =============================================================================
1166 void() fd_secret_move1;
1167 void() fd_secret_move2;
1168 void() fd_secret_move3;
1169 void() fd_secret_move4;
1170 void() fd_secret_move5;
1171 void() fd_secret_move6;
1172 void() fd_secret_done;
1174 float SECRET_OPEN_ONCE = 1; // stays open
1175 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1176 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1177 float SECRET_NO_SHOOT = 8; // only opened by trigger
1178 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1181 void () fd_secret_use =
1185 self.health = 10000;
1186 self.bot_attack = TRUE;
1188 // exit if still moving around...
1189 if (self.origin != self.oldorigin)
1192 self.message = ""; // no more message
1194 SUB_UseTargets(); // fire all targets / killtargets
1196 self.velocity = '0 0 0';
1198 // Make a sound, wait a little...
1200 if (self.noise1 != "")
1201 sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
1202 self.nextthink = self.ltime + 0.1;
1204 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1205 makevectors(self.mangle);
1209 if (self.spawnflags & SECRET_1ST_DOWN)
1210 self.t_width = fabs(v_up * self.size);
1212 self.t_width = fabs(v_right * self.size);
1216 self.t_length = fabs(v_forward * self.size);
1218 if (self.spawnflags & SECRET_1ST_DOWN)
1219 self.dest1 = self.origin - v_up * self.t_width;
1221 self.dest1 = self.origin + v_right * (self.t_width * temp);
1223 self.dest2 = self.dest1 + v_forward * self.t_length;
1224 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1225 if (self.noise2 != "")
1226 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1229 // Wait after first movement...
1230 void () fd_secret_move1 =
1232 self.nextthink = self.ltime + 1.0;
1233 self.think = fd_secret_move2;
1234 if (self.noise3 != "")
1235 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1238 // Start moving sideways w/sound...
1239 void () fd_secret_move2 =
1241 if (self.noise2 != "")
1242 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1243 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1246 // Wait here until time to go back...
1247 void () fd_secret_move3 =
1249 if (self.noise3 != "")
1250 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1251 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1253 self.nextthink = self.ltime + self.wait;
1254 self.think = fd_secret_move4;
1259 void () fd_secret_move4 =
1261 if (self.noise2 != "")
1262 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1263 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1267 void () fd_secret_move5 =
1269 self.nextthink = self.ltime + 1.0;
1270 self.think = fd_secret_move6;
1271 if (self.noise3 != "")
1272 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1275 void () fd_secret_move6 =
1277 if (self.noise2 != "")
1278 sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
1279 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1282 void () fd_secret_done =
1284 if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1286 self.health = 10000;
1287 self.takedamage = DAMAGE_YES;
1288 //self.th_pain = fd_secret_use;
1290 if (self.noise3 != "")
1291 sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
1294 void () secret_blocked =
1296 if (time < self.attack_finished_single)
1298 self.attack_finished_single = time + 0.5;
1299 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1309 void() secret_touch =
1311 if (activator.classname != "player")
1313 if (self.attack_finished_single > time)
1316 self.attack_finished_single = time + 2;
1320 if (other.flags & FL_CLIENT)
1321 centerprint (other, self.message);
1322 sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
1327 /*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1328 Basic secret door. Slides back, then to the side. Angle determines direction.
1329 wait = # of seconds before coming back
1330 1st_left = 1st move is left of arrow
1331 1st_down = 1st move is down from arrow
1332 always_shoot = even if targeted, keep shootable
1333 t_width = override WIDTH to move back (or height if going down)
1334 t_length = override LENGTH to move sideways
1335 "dmg" damage to inflict when blocked (2 default)
1337 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1344 void () func_door_secret =
1346 /*if (!self.deathtype) // map makers can override this
1347 self.deathtype = " got in the way";*/
1353 self.mangle = self.angles;
1354 self.angles = '0 0 0';
1355 self.solid = SOLID_BSP;
1356 self.movetype = MOVETYPE_PUSH;
1357 self.classname = "door";
1358 setmodel (self, self.model); // precision set below
1359 self.effects |= EF_LOWPRECISION;
1360 setorigin (self, self.origin);
1362 self.touch = secret_touch;
1363 self.blocked = secret_blocked;
1365 self.use = fd_secret_use;
1366 if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
1368 self.health = 10000;
1369 self.takedamage = DAMAGE_YES;
1370 self.event_damage = fd_secret_use;
1372 self.oldorigin = self.origin;
1374 self.wait = 5; // 5 seconds before closing