/*====================================================================== STANDARD DOOR FUNCTIONS ======================================================================*/ float DOOR_START_OPEN = 1; // Work the opposite way around float DOOR_SPAWN_TRIGGER = 2; // Spawn open trigger regardless of targetname float DOOR_DONT_LINK = 4; // never link this door with anything float DOOR_GOLD_KEY = 8; // Gold Key float DOOR_SILVER_KEY = 16; // Silver Key float DOOR_TOGGLE = 32; // Need trigger to open AND close /*====================================================================== /*QUAKED func_door (0 .5 .8) ? STARTOPEN SPAWNTRIG DONTLINK GOLD SILVER TOGGLE STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM Door (bmodel) with 2 states -------- KEYS -------- targetname : = "" door automatically opens, != "" requires trigger to open if using entity state system set SPAWN_TRIG spawnflags to fix this problem target : name of target(s) to trigger at the start opening sequence angle : determines the opening direction, use "360" for angle 0 speed : movement speed (def=100) yaw_speed: return movement speed (def=self.speed) wait : wait before returning (def=3, -1 = never return) lip : lip remaining at end of move. (def=8) health : Can be damaged instead of touched dmg : damage to inflict when blocked. (def=2) message : centerprint message when touched (removed when opened) sounds : 0 = silent 1 = stone 2 = base 3 = stone chain 4 = screechy metal 5 = custom locksounds : 0 = talktalk 1 = short rattle, 2 = medium rattle, 3 = long rattle noise : custom sound - door locked sound (targetname+message) noise1 : custom sound - Moving/Open door (looping) noise2 : custom sound - Stop/Close door noise3 : custom sound - Silver/Gold key try lock noise4 : custom sound - Silver/Gold key OPEN customkey: custom key required to open door (1-4 supported values) message2 : custom key message (You need the custom key) _dirt : -1 = will be excluded from dirtmapping _minlight : Minimum light level for any surface of the brush model _mincolor : Minimum light color for any surface (def='1 1 1' RGB) _shadow : Will cast shadows on other models and itself _shadowself : Will cast shadows on itself -------- SPAWNFLAGS -------- STARTOPEN : door opens in reverse state (moved to dest at spawn) SPAWNTRIG : Will spawn trigger around door even if targetname SETUP DONTLINK : Touching doors will not link into a single entity GOLD : Requires gold key to open SILVER : Requires silver key to open TOGGLE : Door waits for trigger between states (if wait=-1 cannot be blocked) STARTOFF : Starts off and waits for trigger -------- NOTES -------- Door (bmodel) with 2 states ======================================================================*/ // Check for overlapping bound boxes float (entity e1, entity e2) EntitiesTouching = { if (e1.mins_x > e2.maxs_x) return FALSE; if (e1.mins_y > e2.maxs_y) return FALSE; if (e1.mins_z > e2.maxs_z) return FALSE; if (e1.maxs_x < e2.mins_x) return FALSE; if (e1.maxs_y < e2.mins_y) return FALSE; if (e1.maxs_z < e2.mins_z) return FALSE; return TRUE; }; //---------------------------------------------------------------------- void() func_door_trigger_touch = { if (self.owner.estate == ESTATE_BLOCK) return; if (self.owner.spawnflags & ENT_STARTOFF) return; if (other.health < 1) return; if (other.classtype == CT_FUNCBREAK) return; if (other.flags & FL_ITEM) return; if (time < self.attack_finished) return; self.attack_finished = time + 1; // Switch to parent (self) of trigger activator = other; self = self.owner; self.estate_use(); // func_door_use }; //---------------------------------------------------------------------- entity(vector fmins, vector fmaxs) func_door_spawn_field = { local entity trigger; local vector t1, t2; // Create trigger entity and link to parent (self) trigger = spawn(); trigger.classtype = CT_FUNCDOORTRIG; trigger.bsporigin = TRUE; trigger.movetype = MOVETYPE_NONE; trigger.solid = SOLID_TRIGGER; trigger.owner = self; trigger.touch = func_door_trigger_touch; t1 = fmins; t2 = fmaxs; // Extend the X/Y size of the trigger +/- 60 units // Return the trigger entity to the previous function setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); return (trigger); }; //---------------------------------------------------------------------- // Link touching func_door entities //---------------------------------------------------------------------- void() func_door_link = { local entity linkdoor, master; local vector cmins, cmaxs; local float loopcondition; // Switch off the entity once the door linking has been done if (self.spawnflags & ENT_STARTOFF) { self.think = self.estate_off; self.nextthink = self.ltime + 0.1; } // Door linking exceptions (self.enemy = already linked) if (self.enemy) return; // Setup bounding box for first door (master) cmins = self.mins; cmaxs = self.maxs; // lets give the do/while a proper exit condition loopcondition = TRUE; // Really important step, defining the master door master = self; linkdoor = self; while (loopcondition) { // Always point all doors in the list to the master door // which is setup outside of the do/while loop self.owner = master; // Make sure all linked doors have the same health/targetname/message if (self.health) master.health = self.health; if (self.targetname != "") master.targetname = self.targetname; if (self.message != "") master.message = self.message; // For some reason the original ID code renamed doors and secret doors // to the same classname string and then created a list of both together // Not sure why anyone would want the two door types linked and it is // certainly not in any of the original ID maps. // Feature disabled - causes no end of state problems linkdoor = find (linkdoor, classname, self.classname); // reached end of list? if (!linkdoor) { self.enemy = master; // make the chain a loop loopcondition = FALSE; // no more doors left to search, switch to the master door // It is always defined as the .owner field self = self.owner; // This is the conditions for a door getting automatic open trigger // Door CANNOT be damaged (shoot trigger) and NO key item required if (self.health == 0 && self.items == 0) { // Damn annoying that the targetname is being used like this because // there could have been a better way to do this type of functionality // == "" door has special trigger around to open door // != "" door requires trigger event to open if (self.targetname == "") self.spawnflags = self.spawnflags | DOOR_SPAWN_TRIGGER; if (self.spawnflags & DOOR_SPAWN_TRIGGER) self.owner.trigger_field = func_door_spawn_field(cmins, cmaxs); } } else { // Check if the bounding boxes are touching if (EntitiesTouching(self,linkdoor)) { // The .enemy field should be empty, otherwise its linked already // which is a bad situation because the chains will be corrupt // This will break the lots of later functions on // Solution - ignore the crosslink entity and carry on if (linkdoor.enemy) { // Exception: No link doors are setup to be linked to themselves! if ( !(linkdoor.spawnflags & DOOR_DONT_LINK)) { dprint("\b[CROSSLINK]\b really bad situation, must fix!\n"); dprint("-----------------------------------------------\n"); eprint(linkdoor.enemy); dprint("-----------------------------------------------\n\n"); } } else { // Setup the next entity (linkdoor) in the list (self.enemy) // and move self forward to the new entity (linkdoor) self.enemy = linkdoor; self = linkdoor; // Expand touch trigger to include new door if (linkdoor.mins_x < cmins_x) cmins_x = linkdoor.mins_x; if (linkdoor.mins_y < cmins_y) cmins_y = linkdoor.mins_y; if (linkdoor.mins_z < cmins_z) cmins_z = linkdoor.mins_z; if (linkdoor.maxs_x > cmaxs_x) cmaxs_x = linkdoor.maxs_x; if (linkdoor.maxs_y > cmaxs_y) cmaxs_y = linkdoor.maxs_y; if (linkdoor.maxs_z > cmaxs_z) cmaxs_z = linkdoor.maxs_z; } } } } }; //---------------------------------------------------------------------- // Phase 4 - Door CLOSED //---------------------------------------------------------------------- void() func_door_hit_bottom = { if (self.estate == ESTATE_OFF) return; sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_BOTTOM; self.frame = 1 - self.frame; //switch textures }; //---------------------------------------------------------------------- // Phase 3 - Door closing //---------------------------------------------------------------------- void() func_door_go_down = { if (self.estate == ESTATE_OFF) return; sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); if (self.max_health) { self.takedamage = DAMAGE_YES; self.health = self.max_health; } self.state = STATE_DOWN; // Add - return speed can now be different, // yaw_speed = speed if nothing defined SUB_CalcMove (self.pos1, self.yaw_speed, func_door_hit_bottom); }; //---------------------------------------------------------------------- // Phase 2 - Door OPEN //---------------------------------------------------------------------- void() func_door_hit_top = { if (self.estate == ESTATE_OFF) return; sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_TOP; // don't come down automatically if (self.spawnflags & DOOR_TOGGLE) return; self.think = func_door_go_down; self.nextthink = self.ltime + self.wait; }; //---------------------------------------------------------------------- // Phase 1 - Door opening //---------------------------------------------------------------------- void() func_door_go_up = { if (self.estate == ESTATE_OFF) return; if (self.state == STATE_UP) return; // already going up if (self.state == STATE_TOP) { // reset top wait time self.nextthink = self.ltime + self.wait; return; } sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); self.state = STATE_UP; SUB_CalcMove (self.pos2, self.speed, func_door_hit_top); SUB_UseTargets(); self.frame = 1 - self.frame; //switch textures }; //---------------------------------------------------------------------- void() func_door_fire = { local entity oself, starte; if (self.estate == ESTATE_BLOCK) return; if (self.owner != self) dprint ("\b[door_fire]\b self.owner != self\n"); // Check for exact state requests if (other.classtype == CT_TRIGDOORSTATE) { if (other.state == 0) { // Is the door closing (DOWN) or closed (BOTTOM)? if (self.state == STATE_DOWN || self.state == STATE_BOTTOM) return; } else { // Is the door opening (UP) or open (TOP)? if (self.state == STATE_UP || self.state == STATE_TOP) return; } } // play use key sound if (self.items) sound (self, CHAN_VOICE, self.noise4, 1, ATTN_NORM); self.message = string_null; // no more message oself = self; if (self.spawnflags & DOOR_TOGGLE) { if (self.state == STATE_UP || self.state == STATE_TOP) { starte = self; do { func_door_go_down (); self = self.enemy; } while ( (self != starte) && (self != world) ); self = oself; return; } } // trigger all paired doors starte = self; do { func_door_go_up (); self = self.enemy; } while ( (self != starte) && (self != world) ); self = oself; }; //---------------------------------------------------------------------- void() func_door_use = { local entity oself; // Deal with STARTOFF functionality first if (self.spawnflags & ENT_STARTOFF) self.estate_on(); else { // Block USE functionality if state wrong if (self.estate & ESTATE_BLOCK) return; // Door messages are for touch functionality ONLY self.message = string_null; self.owner.message = string_null; self.enemy.message = string_null; // Switch to master door oself = self; self = self.owner; func_door_fire (); self = oself; } }; //---------------------------------------------------------------------- void() func_door_killed = { local entity oself; if (self.estate & ESTATE_BLOCK) return; oself = self; self = self.owner; self.health = self.max_health; self.takedamage = DAMAGE_NO; // wil be reset upon return func_door_use (); self = oself; }; //---------------------------------------------------------------------- // Only used for keys and messages //---------------------------------------------------------------------- void() func_door_touch = { if (self.estate & ESTATE_BLOCK) return; if ( !(other.flags & FL_CLIENT) ) return; if (self.owner.attack_finished > time) return; // Block touch trigger for 2 seconds self.owner.attack_finished = time + 2; // Does the door require any keys? if (self.items == 0) { // Any touch message to display? if (self.owner.message != "") { centerprint (other, self.owner.message); // Use custom lock sound (default = talktalk) sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); } } // Check door for keys (silver/gold/custom) else { // Is this a custom locked door? if (self.moditems > 0) { if ( (self.moditems & other.moditems) != self.moditems ) { // Tell the player that a custom key is required // Use the message2 string instead of message1 centerprint (other, self.message2); // Play door locked sound (based on worldspawn) sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); return; } else { // Remove custom key from player/client other.moditems = other.moditems - self.moditems; // Tell the player that the custom key has been removed // Use the key item netname to reference the custom key // These netnames are updated when picking up the key sprint(other, "You used the "); if (self.moditems & IT_CKEY1 && other.ckeyname1 != "") sprint(other, other.ckeyname1); else if (self.moditems & IT_CKEY2 && other.ckeyname2 != "") sprint(other, other.ckeyname2); else if (self.moditems & IT_CKEY3 && other.ckeyname3 != "") sprint(other, other.ckeyname3); else if (self.moditems & IT_CKEY4 && other.ckeyname4 != "") sprint(other, other.ckeyname4); sprint(other, "\n"); } } else { // Does the player have the correct key? (silver/gold) if ( (self.items & other.items) != self.items ) { if (self.owner.items == IT_KEY1) { if (self.message2 != "") centerprint (other, self.message2); else if (self.worldtype == 1) centerprint (other, "You need the silver runekey"); else if (self.worldtype == 2) centerprint (other, "You need the silver keycard"); else centerprint (other, "You need the silver key"); sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); } else { if (self.message2 != "") centerprint (other, self.message2); else if (self.worldtype == 1) centerprint (other, "You need the gold runekey"); else if (self.worldtype == 2) centerprint (other, "You need the gold keycard"); else centerprint (other, "You need the gold key"); sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); } return; } else { // Tell the player that the key has been removed sprint(other, "You used the "); if (other.items & IT_KEY1) sprint(other, "Silver key\n"); else sprint(other, "Gold key\n"); // Take key from player inventory other.items = other.items - self.items; // Check for a custom unlock sound if (self.locksounds > 0) sound (self, CHAN_WEAPON, self.noise, 1, ATTN_NORM); } } // Turn off touch function self.touch = SUB_Null; // turn off touch on pair/master door if (self.enemy) self.enemy.touch = SUB_Null; func_door_use (); } }; //---------------------------------------------------------------------- void() func_door_on = { // Stop re-triggering ON state if (self.estate == ESTATE_ON) return; // No longer need this spawnflag, remove it self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF); self.estate = ESTATE_ON; self.movetype = MOVETYPE_PUSH; self.solid = SOLID_BSP; setmodel (self, self.mdl); self.velocity = '0 0 0'; self.frame = self.frame_override; // Reset to default // Reset Door position if NOT key or working once if (self.items == 0 && self.wait != -1) { setorigin(self, self.pos1); self.state = STATE_BOTTOM; self.think = SUB_Null; } // reset health and damage trigger if (self.max_health > 0) { self.health = self.max_health; self.takedamage = DAMAGE_YES; } }; //---------------------------------------------------------------------- void() func_door_off = { // Stop re-triggering OFF state if (self.estate == ESTATE_OFF) return; self.estate = ESTATE_OFF; self.movetype = MOVETYPE_NONE; self.solid = SOLID_NOT; setmodel (self, ""); self.velocity = '0 0 0'; self.takedamage = DAMAGE_NO; self.think = SUB_Null; // Stop all movement sounds sound (self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM); // Reset Door position if NOT key or working once if (self.items == 0 && self.wait != -1) { setorigin(self, self.pos2); self.state = STATE_BOTTOM; } }; //---------------------------------------------------------------------- void() func_door_disable = { // Block damage function and show alternative texture self.takedamage = DAMAGE_NO; self.frame = 1 - self.frame_override; // Turn off alt textures }; //---------------------------------------------------------------------- void() func_door_reset = { // If the door is still OFF then leave it if (self.spawnflags & ENT_STARTOFF) return; // Restore key functionality if (self.items > 0 ) { self.touch = func_door_touch; // switch on master door as well if (self.enemy) self.enemy.touch = func_door_touch; } // reset to original position setorigin(self, self.pos1); self.state = STATE_BOTTOM; self.attack_finished = 0; self.frame = self.frame_override; // Reset Alt textures // Restore door to ON state self.estate = ESTATE_OFF; self.estate_on(); self.think = SUB_Null; }; //---------------------------------------------------------------------- void() func_door_blocked = { T_Damage (other, self, self, self.dmg, DAMARMOR); // if a door has a negative wait, it would never come back if blocked, // so let it just squash the object to death real fast if (self.wait != -1) { if (self.state == STATE_DOWN) func_door_go_up (); else func_door_go_down (); } }; //---------------------------------------------------------------------- void() func_door = { if (check_bmodel_keys()) return; // Check for bmodel errors // Setup all empty/silent sound files first if (self.noise1 == "") self.noise1 = SOUND_EMPTY; if (self.noise2 == "") self.noise2 = SOUND_EMPTY; if (self.sounds == 1) { self.noise1 = "doors/drclos4.wav"; self.noise2 = "doors/doormv1.wav"; } else if (self.sounds == 2) { self.noise2 = "doors/hydro1.wav"; self.noise1 = "doors/hydro2.wav"; } else if (self.sounds == 3) { self.noise2 = "doors/stndr1.wav"; self.noise1 = "doors/stndr2.wav"; } else if (self.sounds == 4) { self.noise1 = "doors/ddoor2.wav"; self.noise2 = "doors/ddoor1.wav"; } // Check for any self worldtype override if (!self.worldtype) self.worldtype = world.worldtype; // Allow for custom sounds by checking noise3/4 string content first // Locked door sounds (silver, gold and custom keys) if (self.noise3 == "") { if (self.worldtype == 1) self.noise3 = "doors/runetry.wav"; else if (self.worldtype == 2) self.noise3 = "doors/basetry.wav"; else self.noise3 = "doors/medtry.wav"; } if (self.noise4 == "") { if (self.worldtype == 1) self.noise4 = "doors/runeuse.wav"; else if (self.worldtype == 2) self.noise4 = "doors/baseuse.wav"; else self.noise4 = "doors/meduse.wav"; } // locked sound (targetname + message) if (self.noise == "") self.noise = SOUND_TALK; if (self.locksounds == 1) self.noise = "doors/rattle1.wav"; else if (self.locksounds == 2) self.noise = "doors/rattle2.wav"; else if (self.locksounds == 3) self.noise = "doors/rattle6.wav"; else if (self.locksounds == 13) self.noise = "misc/trigger1.wav"; else if (self.locksounds == 14) self.noise = SOUND_EMPTY; else if (self.locksounds == 16) self.noise = "misc/secret3.wav"; //Pre-cache all sounds precache_sound (self.noise); precache_sound (self.noise1); precache_sound (self.noise2); precache_sound (self.noise3); precache_sound (self.noise4); // Setup classtype and flag as BSP origin self.classtype = CT_FUNCDOOR; self.classgroup = CG_FUNCMOVER; self.bsporigin = TRUE; self.mdl = self.model; SetMovedir (); // Work out movedir based on angles if (!self.speed) self.speed = 100; // Second movement speed can be different, yaw_speed if (!self.yaw_speed) self.yaw_speed = self.speed; if (!self.wait) self.wait = 3; if (!self.lip) self.lip = 8; if (!self.dmg) self.dmg = 2; self.state = STATE_BOTTOM; self.attack_finished = 0; // Default/Setup alternative texture frame if (!self.frame_override) self.frame_override = 0; self.frame = self.frame_override; //---------------------------------------------------------------------- // Is the door locked via special keys (silver/gold) // Make sure any rogue entity keys are removed self.items = self.moditems = 0; if (self.spawnflags & DOOR_SILVER_KEY) self.items = IT_KEY1; else if (self.spawnflags & DOOR_GOLD_KEY) self.items = IT_KEY2; // Check for custom key requirement (only supports 1-4 types) else if (self.customkey > 0) { self.items = IT_KEY1 | IT_KEY2; if (self.message2 == "") self.message2 = "You need the custom key"; if (self.customkey == 1) self.moditems = IT_CKEY1; else if (self.customkey == 2) self.moditems = IT_CKEY2; else if (self.customkey == 3) self.moditems = IT_CKEY3; else if (self.customkey == 4) self.moditems = IT_CKEY4; else { dprint("\b[FUNC_DOOR]\b Custom key value not supported\n"); self.moditems = self.items = 0; } } // key doors ONLY work once and have touch function if (self.items > 0) { self.wait = -1; // Work once self.health = 0; // no shoot/damage } //---------------------------------------------------------------------- // Add bmodel to world and work out movement positions self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setmodel (self, self.mdl); setorigin (self, self.origin); setsize (self, self.mins , self.maxs); self.pos1 = self.origin; self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); // Check for spawning conditions (nightmare, coop) // Needs to exist after entity has been added to work for BSPorigin if (check_nightmare() == TRUE) return; if (check_coop() == TRUE) return; // Only need door functionality if require keys or centerprint messages if (self.items > 0 || self.message != "") self.touch = func_door_touch; self.blocked = func_door_blocked; //---------------------------------------------------------------------- // DOOR_START_OPEN is design for an entity to be lit in the closed position // but spawn in the open position, also reversed door functions if (self.spawnflags & DOOR_START_OPEN) { setorigin (self, self.pos2); self.pos2 = self.pos1; self.pos1 = self.origin; } // Only enable damage trigger (die) if health set if (self.health) { self.max_health = self.health; self.takedamage = DAMAGE_YES; self.th_die = func_door_killed; } // Setup Entity State functionality // The 'USE' wrapper has to be setup, because after // the doors are linked they will all have targetnames! self.use = entity_state_use; self.estate_on = func_door_on; self.estate_off = func_door_off; self.estate_use = func_door_use; self.estate_disable = func_door_disable; self.estate_reset = func_door_reset; self.estate = ESTATE_ON; //---------------------------------------------------------------------- // Doors cannot be linked until all bmodels have spawned and // bounding box sizes established, check for exceptions first if (self.spawnflags & DOOR_DONT_LINK) { self.owner = self.enemy = self; // Its safe to switch this entity off, no linking required if (self.spawnflags & ENT_STARTOFF) self.estate_off(); } else { self.think = func_door_link; self.nextthink = self.ltime + 0.1; } }; /*====================================================================== /*QUAKED trigger_doorstate (0.5 0 0.5) (-8 -8 -16) (8 8 16) x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Trigger func_door(s) to exact state (open/close) -------- KEYS -------- targetname : trigger this entity target : ALL target(s) must be func_door entities with toggle spawnflag! state : 0 or 1 func_door state (0=closed, 1=open) wait : -1 = will only fire targets once -------- SPAWNFLAGS -------- STARTOFF : Requires trigger to activate -------- NOTES -------- Trigger func_door(s) to exact state (open/close) ======================================================================*/ void() trigger_doorstate_use = { if (self.estate & ESTATE_BLOCK) return; if (self.attack_finished > time) return; // Fire target(s) trigger_strs(self.target, self); // Setup to trigger once? if (self.wait < 0) self.attack_finished = LARGE_TIMER; }; //---------------------------------------------------------------------- void() trigger_doorstate_delay = { if (self.estate & ESTATE_BLOCK) return; // Remove the trigger delay function if (self.spawnflags & ENT_STARTOFF) self.spawnflags = self.spawnflags - ENT_STARTOFF; // Re-route use function to actual counter self.estate_use = trigger_counter_use; }; //---------------------------------------------------------------------- void() trigger_doorstate_setup = { // entity check condition self.lefty = FALSE; // Search for func_door targets self.enemy = find(world, targetname, self.target); // Found any yet? while (self.enemy) { // Found a func_door? if (self.enemy.classtype == CT_FUNCDOOR) { // All func_door(s) require toggle spawnflag enabled if (self.enemy.spawnflags & DOOR_TOGGLE) self.lefty = TRUE; else { dprint("\b[TRIG_DOORSTATE]\b "); dprint(self.enemy.targetname); dprint(" missing toggle spawnflag!\n"); } } // The whole list needs to be door entities else { dprint("\b[TRIG_DOORSTATE]\b Target ("); dprint(self.target); dprint(") ("); dprint(self.enemy.classname); dprint(") Wrong!\n"); self.lefty = FALSE; } // Keep looping through the find list self.enemy = find(self.enemy, targetname, self.target); } // Found any errors with setup? if (self.lefty == FALSE) { spawn_marker(self.origin, SPNMARK_YELLOW); remove(self); return; } // Finally setup Entity State functionality if (self.targetname != "") self.use = entity_state_use; // The delay function is not switched off, its activate to use if (self.spawnflags & ENT_STARTOFF) self.estate_use = trigger_doorstate_delay; else self.estate_use = trigger_doorstate_use; }; //---------------------------------------------------------------------- void() trigger_doorstate = { self.classtype = CT_TRIGDOORSTATE; if (self.state < 0 || self.state > 1) self.state = 0; self.delay = self.sounds = 0; self.message = self.noise = ""; self.nodebuginfo = TRUE; // This entity is called a lot from pressure plate entities // so it really needs to be setup right before use! // No target = hard error and warning message if (self.target == "") { dprint("\b[TRIG_DOORSTATE]\b Missing target!\n"); spawn_marker(self.origin, SPNMARK_YELLOW); remove(self); return; } // Check for firing conditions (nightmare, coop) if (check_nightmare() == TRUE) return; if (check_coop() == TRUE) return; // Double check targets are right type self.think = trigger_doorstate_setup; self.nextthink = time + 0.2; };