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; // already 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 if not(InitMovingBrushTrigger())
203 self.effects |= EF_LOWPRECISION;
204 setsize (self, self.mins , self.maxs);
206 self.blocked = plat_crush;
211 self.pos1 = self.origin;
212 self.pos2 = self.origin;
213 self.pos2_z = self.origin_z - self.size_z + 8;
215 self.use = plat_trigger_use;
217 plat_spawn_inside_trigger (); // the "start moving" trigger
225 InitializeEntity(self, plat_init_movedown, INITPRIO_SETLOCATION);
232 self.think = train_next;
233 self.nextthink = self.ltime + self.wait;
239 targ = find(world, targetname, self.target);
240 self.target = targ.target;
242 objerror("train_next: no next target");
243 self.wait = targ.wait;
249 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
251 SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
256 SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
258 SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
262 void func_train_find()
265 targ = find(world, targetname, self.target);
266 self.target = targ.target;
268 objerror("func_train_find: no next target");
269 setorigin(self, targ.origin - self.mins);
270 self.nextthink = self.ltime + 1;
271 self.think = train_next;
274 /*QUAKED spawnfunc_func_train (0 .5 .8) ?
275 Ridable platform, targets spawnfunc_path_corner path to follow.
276 speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
277 target : targetname of first spawnfunc_path_corner (starts here)
279 void spawnfunc_func_train()
282 objerror("func_train without a target");
286 if not(InitMovingBrushTrigger())
288 self.effects |= EF_LOWPRECISION;
290 // wait for targets to spawn
291 InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
296 void rotating_blocked()
299 if(self.dmg && other.takedamage != DAMAGE_NO) {
300 if(self.dmgtime2 < time) {
301 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
302 self.dmgtime2 = time + self.dmgtime;
305 // Gib dead/dying stuff
306 if(other.deadflag != DEAD_NO)
307 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
313 /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
314 Brush model that spins in place on one axis (default Z).
315 speed : speed to rotate (in degrees per second)
316 noise : path/name of looping .wav file to play.
317 dmg : Do this mutch dmg every .dmgtime intervall when blocked
321 void spawnfunc_func_rotating()
325 precache_sound(self.noise);
326 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
330 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
331 if (self.spawnflags & 4) // X (untested)
332 self.avelocity = '0 0 1' * self.speed;
333 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
334 else if (self.spawnflags & 8) // Y (untested)
335 self.avelocity = '1 0 0' * self.speed;
336 // FIXME: test if this turns the right way, then remove this comment (negate as needed)
338 self.avelocity = '0 1 0' * self.speed;
340 if(self.dmg & (!self.message))
341 self.message = " was squished";
342 if(self.dmg && (!self.message2))
343 self.message2 = "was squished by";
346 if(self.dmg && (!self.dmgtime))
349 self.dmgtime2 = time;
351 if not(InitMovingBrushTrigger())
353 // no EF_LOWPRECISION here, as rounding angles is bad
355 self.blocked = rotating_blocked;
357 // wait for targets to spawn
358 self.nextthink = self.ltime + 999999999;
359 self.think = SUB_Null;
363 void func_bobbing_controller_think()
366 self.nextthink = time + 0.1;
367 // calculate sinewave using makevectors
368 makevectors((time * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
369 v = self.owner.destvec + self.owner.movedir * v_forward_y;
370 // * 10 so it will arrive in 0.1 sec
371 self.owner.velocity = (v - self.owner.origin) * 10;
374 void bobbing_blocked()
376 // no need to duplicate code
380 /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
381 Brush model that moves back and forth on one axis (default Z).
382 speed : how long one cycle takes in seconds (default 4)
383 height : how far the cycle moves (default 32)
384 phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
385 noise : path/name of looping .wav file to play.
386 dmg : Do this mutch dmg every .dmgtime intervall when blocked
389 void spawnfunc_func_bobbing()
391 local entity controller;
394 precache_sound(self.noise);
395 ambientsound(self.origin, self.noise, VOL_BASE, ATTN_IDLE);
401 // center of bobbing motion
402 self.destvec = self.origin;
403 // time scale to get degrees
404 self.cnt = 360 / self.speed;
406 // damage when blocked
407 self.blocked = bobbing_blocked;
408 if(self.dmg & (!self.message))
409 self.message = " was squished";
410 if(self.dmg && (!self.message2))
411 self.message2 = "was squished by";
412 if(self.dmg && (!self.dmgtime))
414 self.dmgtime2 = time;
417 if (self.spawnflags & 1) // X
418 self.movedir = '1 0 0' * self.height;
419 else if (self.spawnflags & 2) // Y
420 self.movedir = '0 1 0' * self.height;
422 self.movedir = '0 0 1' * self.height;
424 if not(InitMovingBrushTrigger())
427 // wait for targets to spawn
428 controller = spawn();
429 controller.classname = "func_bobbing_controller";
430 controller.owner = self;
431 controller.nextthink = time + 1;
432 controller.think = func_bobbing_controller_think;
433 self.nextthink = self.ltime + 999999999;
434 self.think = SUB_Null;
436 // Savage: Reduce bandwith, critical on e.g. nexdm02
437 self.effects |= EF_LOWPRECISION;
440 // button and multiple button
443 void() button_return;
447 self.state = STATE_TOP;
448 self.nextthink = self.ltime + self.wait;
449 self.think = button_return;
450 activator = self.enemy;
452 self.frame = 1; // use alternate textures
457 self.state = STATE_BOTTOM;
462 self.state = STATE_DOWN;
463 SUB_CalcMove (self.pos1, self.speed, button_done);
464 self.frame = 0; // use normal textures
466 self.takedamage = DAMAGE_YES; // can be shot again
470 void button_blocked()
472 // do nothing, just don't come all the way back out
478 self.health = self.max_health;
479 self.takedamage = DAMAGE_NO; // will be reset upon return
481 if (self.state == STATE_UP || self.state == STATE_TOP)
484 if (self.noise != "")
485 sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
487 self.state = STATE_UP;
488 SUB_CalcMove (self.pos2, self.speed, button_wait);
494 // if (activator.classname != "player")
496 // dprint(activator.classname);
497 // dprint(" triggered a button\n");
499 self.enemy = activator;
505 // if (activator.classname != "player")
507 // dprint(activator.classname);
508 // dprint(" touched a button\n");
512 if not(other.iscreature)
514 if(other.velocity * self.movedir < 0)
518 self.enemy = other.owner;
522 void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
524 self.health = self.health - damage;
525 if (self.health <= 0)
527 // if (activator.classname != "player")
529 // dprint(activator.classname);
530 // dprint(" killed a button\n");
532 self.enemy = damage_attacker;
538 /*QUAKED spawnfunc_func_button (0 .5 .8) ?
539 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.
541 "angle" determines the opening direction
542 "target" all entities with a matching targetname will be used
543 "speed" override the default 40 speed
544 "wait" override the default 1 second wait (-1 = never return)
545 "lip" override the default 4 pixel lip remaining at end of move
546 "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
553 void spawnfunc_func_button()
557 if not(InitMovingBrushTrigger())
559 self.effects |= EF_LOWPRECISION;
561 self.blocked = button_blocked;
562 self.use = button_use;
564 // if (self.health == 0) // all buttons are now shootable
568 self.max_health = self.health;
569 self.event_damage = button_damage;
570 self.takedamage = DAMAGE_YES;
573 self.touch = button_touch;
583 precache_sound(self.noise);
585 self.state = STATE_BOTTOM;
587 self.pos1 = self.origin;
588 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
592 float DOOR_START_OPEN = 1;
593 float DOOR_DONT_LINK = 4;
594 float DOOR_TOGGLE = 32;
598 Doors are similar to buttons, but can spawn a fat trigger field around them
599 to open without a touch, and they link together to form simultanious
602 Door.owner is the master door. If there is only one door, it points to itself.
603 If multiple doors, all will point to a single one.
605 Door.enemy chains from the master door through all doors linked in the chain.
610 =============================================================================
614 =============================================================================
619 void() door_rotating_go_down;
620 void() door_rotating_go_up;
625 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
626 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
629 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
630 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
632 //Dont chamge direction for dead or dying stuff
633 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
636 if (self.state == STATE_DOWN)
637 if (self.classname == "door")
642 door_rotating_go_up ();
645 if (self.classname == "door")
650 door_rotating_go_down ();
654 //gib dying stuff just to make sure
655 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
656 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
660 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
661 // if a door has a negative wait, it would never come back if blocked,
662 // so let it just squash the object to death real fast
663 /* if (self.wait >= 0)
665 if (self.state == STATE_DOWN)
676 if (self.noise1 != "")
677 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
678 self.state = STATE_TOP;
679 if (self.spawnflags & DOOR_TOGGLE)
680 return; // don't come down automatically
681 if (self.classname == "door")
683 self.think = door_go_down;
686 self.think = door_rotating_go_down;
688 self.nextthink = self.ltime + self.wait;
691 void door_hit_bottom()
693 if (self.noise1 != "")
694 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
695 self.state = STATE_BOTTOM;
700 if (self.noise2 != "")
701 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
704 self.takedamage = DAMAGE_YES;
705 self.health = self.max_health;
708 self.state = STATE_DOWN;
709 SUB_CalcMove (self.pos1, self.speed, door_hit_bottom);
714 if (self.state == STATE_UP)
715 return; // already going up
717 if (self.state == STATE_TOP)
718 { // reset top wait time
719 self.nextthink = self.ltime + self.wait;
723 if (self.noise2 != "")
724 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
725 self.state = STATE_UP;
726 SUB_CalcMove (self.pos2, self.speed, door_hit_top);
729 oldmessage = self.message;
732 self.message = oldmessage;
737 =============================================================================
741 =============================================================================
749 if (self.owner != self)
750 objerror ("door_fire: self.owner != self");
754 if (self.spawnflags & DOOR_TOGGLE)
756 if (self.state == STATE_UP || self.state == STATE_TOP)
761 if (self.classname == "door")
767 door_rotating_go_down ();
770 } while ( (self != starte) && (self != world) );
776 // trigger all paired doors
780 if (self.classname == "door")
785 // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
786 if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
788 self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
789 self.pos2 = '0 0 0' - self.pos2;
791 // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
792 if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN
793 && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
795 door_rotating_go_up ();
799 } while ( (self != starte) && (self != world) );
808 //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
819 void door_trigger_touch()
821 if (other.health < 1)
822 if not(other.iscreature && other.deadflag == DEAD_NO)
825 if (time < self.attack_finished_single)
827 self.attack_finished_single = time + 1;
836 void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
839 self.health = self.health - damage;
840 if (self.health <= 0)
844 self.health = self.max_health;
845 self.takedamage = DAMAGE_NO; // wil be reset upon return
861 if(other.classname != "player")
863 if (self.owner.attack_finished_single > time)
866 self.owner.attack_finished_single = time + 2;
868 if (!(self.owner.dmg) && (self.owner.message != ""))
870 if (other.flags & FL_CLIENT)
871 centerprint (other, self.owner.message);
872 play2(other, "misc/talk.wav");
877 void door_rotating_blocked()
880 if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
881 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
884 if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite?
885 Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
887 //Dont chamge direction for dead or dying stuff
888 if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
891 if (self.state == STATE_DOWN)
892 door_rotating_go_up ();
894 door_rotating_go_down ();
897 //gib dying stuff just to make sure
898 if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite?
899 Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
903 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
904 // if a door has a negative wait, it would never come back if blocked,
905 // so let it just squash the object to death real fast
906 /* if (self.wait >= 0)
908 if (self.state == STATE_DOWN)
909 door_rotating_go_up ();
911 door_rotating_go_down ();
917 void door_rotating_hit_top()
919 if (self.noise1 != "")
920 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
921 self.state = STATE_TOP;
922 if (self.spawnflags & DOOR_TOGGLE)
923 return; // don't come down automatically
924 self.think = door_rotating_go_down;
925 self.nextthink = self.ltime + self.wait;
928 void door_rotating_hit_bottom()
930 if (self.noise1 != "")
931 sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
932 if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
934 self.pos2 = '0 0 0' - self.pos2;
937 self.state = STATE_BOTTOM;
940 void door_rotating_go_down()
942 if (self.noise2 != "")
943 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
946 self.takedamage = DAMAGE_YES;
947 self.health = self.max_health;
950 self.state = STATE_DOWN;
951 SUB_CalcAngleMove (self.pos1, self.speed, door_rotating_hit_bottom);
954 void door_rotating_go_up()
956 if (self.state == STATE_UP)
957 return; // already going up
959 if (self.state == STATE_TOP)
960 { // reset top wait time
961 self.nextthink = self.ltime + self.wait;
964 if (self.noise2 != "")
965 sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
966 self.state = STATE_UP;
967 SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
970 oldmessage = self.message;
973 self.message = oldmessage;
980 =============================================================================
984 =============================================================================
988 entity spawn_field(vector fmins, vector fmaxs)
990 local entity trigger;
994 trigger.classname = "doortriggerfield";
995 trigger.movetype = MOVETYPE_NONE;
996 trigger.solid = SOLID_TRIGGER;
997 trigger.owner = self;
998 trigger.touch = door_trigger_touch;
1002 setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
1007 float EntitiesTouching(entity e1, entity e2)
1009 if (e1.absmin_x > e2.absmax_x)
1011 if (e1.absmin_y > e2.absmax_y)
1013 if (e1.absmin_z > e2.absmax_z)
1015 if (e1.absmax_x < e2.absmin_x)
1017 if (e1.absmax_y < e2.absmin_y)
1019 if (e1.absmax_z < e2.absmin_z)
1034 local entity t, starte;
1035 local vector cmins, cmaxs;
1038 return; // already linked by another door
1039 if (self.spawnflags & 4)
1041 self.owner = self.enemy = self;
1049 self.trigger_field = spawn_field(self.absmin, self.absmax);
1051 return; // don't want to link this door
1054 cmins = self.absmin;
1055 cmaxs = self.absmax;
1062 self.owner = starte; // master door
1065 starte.health = self.health;
1067 starte.targetname = self.targetname;
1068 if (self.message != "")
1069 starte.message = self.message;
1071 t = find(t, classname, self.classname);
1074 self.enemy = starte; // make the chain a loop
1076 // shootable, or triggered doors just needed the owner/enemy links,
1077 // they don't spawn a field
1088 self.owner.trigger_field = spawn_field(cmins, cmaxs);
1093 if (EntitiesTouching(self,t))
1096 objerror ("cross connected doors");
1101 if (t.absmin_x < cmins_x)
1102 cmins_x = t.absmin_x;
1103 if (t.absmin_y < cmins_y)
1104 cmins_y = t.absmin_y;
1105 if (t.absmin_z < cmins_z)
1106 cmins_z = t.absmin_z;
1107 if (t.absmax_x > cmaxs_x)
1108 cmaxs_x = t.absmax_x;
1109 if (t.absmax_y > cmaxs_y)
1110 cmaxs_y = t.absmax_y;
1111 if (t.absmax_z > cmaxs_z)
1112 cmaxs_z = t.absmax_z;
1119 /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
1120 if two doors touch, they are assumed to be connected and operate as a unit.
1122 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1124 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 useful for touch or takedamage doors).
1126 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1127 "angle" determines the opening direction
1128 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1129 "health" if set, door must be shot open
1130 "speed" movement speed (100 default)
1131 "wait" wait before returning (3 default, -1 = never return)
1132 "lip" lip remaining at end of move (8 default)
1133 "dmg" damage to inflict when blocked (2 default)
1140 FIXME: only one sound set available at the time being
1144 void door_init_startopen()
1146 setorigin (self, self.pos2);
1147 self.pos2 = self.pos1;
1148 self.pos1 = self.origin;
1151 void spawnfunc_func_door()
1153 //if (!self.deathtype) // map makers can override this
1154 // self.deathtype = " got in the way";
1157 self.max_health = self.health;
1158 if not(InitMovingBrushTrigger())
1160 self.effects |= EF_LOWPRECISION;
1161 self.classname = "door";
1163 self.blocked = door_blocked;
1164 self.use = door_use;
1166 if(self.spawnflags & 8)
1169 if(self.dmg && (!self.message))
1170 self.message = "was squished";
1171 if(self.dmg && (!self.message2))
1172 self.message2 = "was squished by";
1174 if (self.sounds > 0)
1176 precache_sound ("plats/medplat1.wav");
1177 precache_sound ("plats/medplat2.wav");
1178 self.noise2 = "plats/medplat1.wav";
1179 self.noise1 = "plats/medplat2.wav";
1189 self.pos1 = self.origin;
1190 self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
1192 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1193 // but spawn in the open position
1194 if (self.spawnflags & DOOR_START_OPEN)
1195 InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
1197 self.state = STATE_BOTTOM;
1201 self.takedamage = DAMAGE_YES;
1202 self.event_damage = door_damage;
1208 self.touch = door_touch;
1210 // LinkDoors can't be done until all of the doors have been spawned, so
1211 // the sizes can be detected properly.
1212 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1215 /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
1216 if two doors touch, they are assumed to be connected and operate as a unit.
1218 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1220 BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
1221 The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
1222 must have set trigger_reverse to 1.
1223 BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
1225 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).
1227 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1228 "angle" determines the destination angle for opening. negative values reverse the direction.
1229 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1230 "health" if set, door must be shot open
1231 "speed" movement speed (100 default)
1232 "wait" wait before returning (3 default, -1 = never return)
1233 "dmg" damage to inflict when blocked (2 default)
1240 FIXME: only one sound set available at the time being
1243 void door_rotating_init_startopen()
1245 self.angles = self.movedir;
1246 self.pos2 = '0 0 0';
1247 self.pos1 = self.movedir;
1251 void spawnfunc_func_door_rotating()
1254 //if (!self.deathtype) // map makers can override this
1255 // self.deathtype = " got in the way";
1257 // I abuse "movedir" for denoting the axis for now
1258 if (self.spawnflags & 64) // X (untested)
1259 self.movedir = '0 0 1';
1260 else if (self.spawnflags & 128) // Y (untested)
1261 self.movedir = '1 0 0';
1263 self.movedir = '0 1 0';
1265 if (self.angles_y==0) self.angles_y = 90;
1267 self.movedir = self.movedir * self.angles_y;
1268 self.angles = '0 0 0';
1270 self.max_health = self.health;
1271 if not(InitMovingBrushTrigger())
1273 //self.effects |= EF_LOWPRECISION;
1274 self.classname = "door_rotating";
1276 self.blocked = door_blocked;
1277 self.use = door_use;
1279 if(self.spawnflags & 8)
1282 if(self.dmg && (!self.message))
1283 self.message = "was squished";
1284 if(self.dmg && (!self.message2))
1285 self.message2 = "was squished by";
1287 if (self.sounds > 0)
1289 precache_sound ("plats/medplat1.wav");
1290 precache_sound ("plats/medplat2.wav");
1291 self.noise2 = "plats/medplat1.wav";
1292 self.noise1 = "plats/medplat2.wav";
1299 self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
1301 self.pos1 = '0 0 0';
1302 self.pos2 = self.movedir;
1304 // DOOR_START_OPEN is to allow an entity to be lighted in the closed position
1305 // but spawn in the open position
1306 if (self.spawnflags & DOOR_START_OPEN)
1307 InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
1309 self.state = STATE_BOTTOM;
1313 self.takedamage = DAMAGE_YES;
1314 self.event_damage = door_damage;
1320 self.touch = door_touch;
1322 // LinkDoors can't be done until all of the doors have been spawned, so
1323 // the sizes can be detected properly.
1324 InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
1328 =============================================================================
1332 =============================================================================
1335 void() fd_secret_move1;
1336 void() fd_secret_move2;
1337 void() fd_secret_move3;
1338 void() fd_secret_move4;
1339 void() fd_secret_move5;
1340 void() fd_secret_move6;
1341 void() fd_secret_done;
1343 float SECRET_OPEN_ONCE = 1; // stays open
1344 float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
1345 float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
1346 float SECRET_NO_SHOOT = 8; // only opened by trigger
1347 float SECRET_YES_SHOOT = 16; // shootable even if targeted
1350 void fd_secret_use()
1353 string message_save;
1355 self.health = 10000;
1356 self.bot_attack = TRUE;
1358 // exit if still moving around...
1359 if (self.origin != self.oldorigin)
1362 message_save = self.message;
1363 self.message = ""; // no more message
1364 SUB_UseTargets(); // fire all targets / killtargets
1365 self.message = message_save;
1367 self.velocity = '0 0 0';
1369 // Make a sound, wait a little...
1371 if (self.noise1 != "")
1372 sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
1373 self.nextthink = self.ltime + 0.1;
1375 temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
1376 makevectors(self.mangle);
1380 if (self.spawnflags & SECRET_1ST_DOWN)
1381 self.t_width = fabs(v_up * self.size);
1383 self.t_width = fabs(v_right * self.size);
1387 self.t_length = fabs(v_forward * self.size);
1389 if (self.spawnflags & SECRET_1ST_DOWN)
1390 self.dest1 = self.origin - v_up * self.t_width;
1392 self.dest1 = self.origin + v_right * (self.t_width * temp);
1394 self.dest2 = self.dest1 + v_forward * self.t_length;
1395 SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
1396 if (self.noise2 != "")
1397 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1400 // Wait after first movement...
1401 void fd_secret_move1()
1403 self.nextthink = self.ltime + 1.0;
1404 self.think = fd_secret_move2;
1405 if (self.noise3 != "")
1406 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1409 // Start moving sideways w/sound...
1410 void fd_secret_move2()
1412 if (self.noise2 != "")
1413 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1414 SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
1417 // Wait here until time to go back...
1418 void fd_secret_move3()
1420 if (self.noise3 != "")
1421 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1422 if (!(self.spawnflags & SECRET_OPEN_ONCE))
1424 self.nextthink = self.ltime + self.wait;
1425 self.think = fd_secret_move4;
1430 void fd_secret_move4()
1432 if (self.noise2 != "")
1433 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1434 SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
1438 void fd_secret_move5()
1440 self.nextthink = self.ltime + 1.0;
1441 self.think = fd_secret_move6;
1442 if (self.noise3 != "")
1443 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1446 void fd_secret_move6()
1448 if (self.noise2 != "")
1449 sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
1450 SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
1453 void fd_secret_done()
1455 if (self.spawnflags&SECRET_YES_SHOOT)
1457 self.health = 10000;
1458 self.takedamage = DAMAGE_YES;
1459 //self.th_pain = fd_secret_use;
1461 if (self.noise3 != "")
1462 sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
1465 void secret_blocked()
1467 if (time < self.attack_finished_single)
1469 self.attack_finished_single = time + 0.5;
1470 //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
1482 if not(other.iscreature)
1484 if (self.attack_finished_single > time)
1487 self.attack_finished_single = time + 2;
1491 if (other.flags & FL_CLIENT)
1492 centerprint (other, self.message);
1493 play2(other, "misc/talk.wav");
1498 /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
1499 Basic secret door. Slides back, then to the side. Angle determines direction.
1500 wait = # of seconds before coming back
1501 1st_left = 1st move is left of arrow
1502 1st_down = 1st move is down from arrow
1503 always_shoot = even if targeted, keep shootable
1504 t_width = override WIDTH to move back (or height if going down)
1505 t_length = override LENGTH to move sideways
1506 "dmg" damage to inflict when blocked (2 default)
1508 If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
1515 void spawnfunc_func_door_secret()
1517 /*if (!self.deathtype) // map makers can override this
1518 self.deathtype = " got in the way";*/
1524 self.mangle = self.angles;
1525 self.angles = '0 0 0';
1526 self.classname = "door";
1527 if not(InitMovingBrushTrigger())
1529 self.effects |= EF_LOWPRECISION;
1531 self.touch = secret_touch;
1532 self.blocked = secret_blocked;
1534 self.use = fd_secret_use;
1539 self.spawnflags |= SECRET_YES_SHOOT;
1541 if(self.spawnflags&SECRET_YES_SHOOT)
1543 self.health = 10000;
1544 self.takedamage = DAMAGE_YES;
1545 self.event_damage = fd_secret_use;
1547 self.oldorigin = self.origin;
1549 self.wait = 5; // 5 seconds before closing