//====================================================================== // Trigger/use all entities that match the supplied targetname // self is switched around so target use function works correctly // //---------------------------------------------------------------------- void(string target_strs, entity activator_ent) trigger_strs = { local entity tself, tother, tactivator, ent_list; // Some entities constantly use this function (<0.1s trigger) // This is just an entity key to block the debug info if (activator_ent.nodebuginfo == FALSE) { dprint("\b[STR_TRIG]\b Str ("); dprint(target_strs); dprint(") Activator ("); dprint(activator_ent.classname); if (target_strs == "") dprint(") - ERROR!!\n"); else dprint(")\n"); } // Prevent this function from triggering everything by // checking if the target_str is empty (empty = world) if (target_strs != "") { // Store self/other for later tself = self; tother = other; tactivator = activator; if (activator_ent.flags & FL_CLIENT) activator = activator_ent; // Build initial list from world ent_list = find(world, targetname, target_strs); // Cycle through list while(ent_list) { // Setup self/other ready for trigger/use function self = ent_list; other = tself; if (self.use) { self.activate = activator_ent; // Link activator self.use(); // Fire use function } // Find next trigger from intial list (not world) ent_list = find(ent_list, targetname, target_strs); } // Restore self/other from previous temp storage self = tself; other = tother; if (activator_ent.flags & FL_CLIENT) activator = tactivator; } }; //====================================================================== // Trigger/use the entity supplied and activator // self is switched around so target use function works correctly // //---------------------------------------------------------------------- void(entity target_ent, entity activator_ent) trigger_ent = { local entity tself, tother, tactivator; // Some entities constantly use this function (<0.1s trigger) // This is just an entity key to block the debug info if (activator_ent.nodebuginfo == FALSE) { dprint("\b[ENT_TRIG]\b Ent ("); dprint(ftos(target_ent.classtype)); dprint(") Activator ("); dprint(activator_ent.classname); if (!target_ent) dprint(") - ERROR!!\n"); else dprint(")\n"); } if (target_ent && target_ent.use) { // Save self/other for later tself = self; tother = other; tactivator = activator; if (activator_ent.flags & FL_CLIENT) activator = activator_ent; // Switch around self/other for USE function self = target_ent; other = tself; // Use trigger if (self.use) { self.activate = activator_ent; // Link activator self.use(); // Fire use function } // Restore self/other from previous temp storage self = tself; other = tother; if (activator_ent.flags & FL_CLIENT) activator = tactivator; } }; //====================================================================== // Check if the entity has solid surface below // There is no entity type checks, can be any entity in theory! // First check is checkbottom for completel bounding box coverage // Second check is trace down to find floor surface below // Typical trace down distance is 128 units with 24 unit difference // // Uses for this function:- // ai_states.qc - ai_stand // monsters.qc - monster_deadbody_check // ai_gibs.qc - CheckFloorGib // items.qc - item_thinkloop // //---------------------------------------------------------------------- void(entity source, float floor_dist) ent_floorcheck = { local vector trace_pos1; local float trace_dist; // if (!checkbottom(source)) = disabled // too expensive, 5 traces (4 corners + center) and liquid check // Tracedown directly from origin for floor check trace_pos1 = source.origin; trace_pos1_z = trace_pos1_z - FLOOR_TRACE_DIST; traceline (source.origin, trace_pos1, TRUE, source); // Work out distance from origin to floor trace_dist = source.origin_z - trace_endpos_z; // Check if the floor is too far away? (usually 24 units) if (trace_dist > floor_dist && source.flags & FL_ONGROUND) { // Move entity up and do a trace/fall down (256 units max) source.oldorigin = source.origin; source.origin_z = source.origin_z + 1; droptofloor(); if (source.oldorigin != source.origin) { // lift entity upward and remove onground flag source.flags = source.flags - (source.flags & FL_ONGROUND); source.velocity_z = 10 + 10*random(); // Check for any attachment (lids) for ammo if (source.classgroup == CG_AMMOITEM && source.attachment) { // sync flag/velocity so lid falls correctly source.attachment.flags = source.attachment.flags - (source.attachment.flags & FL_ONGROUND); source.attachment.velocity = source.velocity; } } // Move item back to original pos before droptofloor setorigin(source, source.oldorigin); } }; //====================================================================== // Check for contact with sky brushes // - Use pointcontent() for Typical/Enhanced Quake engines // - Use new FTE string functions and surface check for Darkplaces engine // //---------------------------------------------------------------------- float(vector targ_origin) check_skycontent = { local float surfnum; local string texturename; // Does the engine supports extended surface checks? if (ext_dpsurf) { surfnum = getsurfacenearpoint(world, targ_origin); if (surfnum >= 0) { texturename = getsurfacetexture(world, surfnum); if (strncasecmp(texturename, "SKY", 3) == 0) return TRUE; } } else { // Original method for checking sky content (Fitz engines) if (pointcontents(targ_origin) == CONTENT_SKY) return TRUE; } return FALSE; }; //---------------------------------------------------------------------- // Check point content for SOLID or SKY problems //---------------------------------------------------------------------- float(vector pc_org) entity_pcontent = { local float pcontent; pcontent = pointcontents(pc_org); if (pcontent == CONTENT_SOLID) return TRUE; if (check_skycontent(pc_org)) return TRUE; return FALSE; } //====================================================================== // Updates totals on the HUD (checks for client to exist) // - Specify which total to update using constant // - Uses MSG_ONE because it is more reliable across many clients // - Other client updates (SVC_KILLEDMONSTER, SVC_FOUNDSECRET) // //---------------------------------------------------------------------- void(float hud_item) update_hud_totals = { // Has the client player been setup yet? if (client_ent && client_ent.flags & FL_CLIENT) { // Update total secrets if (hud_item & HUD_SECRETS) { msg_entity = client_ent; WriteByte (MSG_ONE, SVC_UPDATESTAT); WriteByte (MSG_ONE, STAT_TOTALSECRETS); WriteLong (MSG_ONE, total_secrets); } // Update total monsters if (hud_item & HUD_MONSTERS) { msg_entity = client_ent; WriteByte (MSG_ONE, SVC_UPDATESTAT); WriteByte (MSG_ONE, STAT_TOTALMONSTERS); WriteLong (MSG_ONE, total_monsters); } } }; //====================================================================== // Reduce any entity down to a non-interactive empty marker // //---------------------------------------------------------------------- void(entity targ) entity_hide = { targ.use = targ.touch = SUB_Null; // No more touchy targ.think = SUB_Null; // No more thinking/animation targ.nextthink = -1; // Never fire think targ.estate = ESTATE_OFF; // Entity state off targ.modelindex = 0; // Make sure no model setmodel (targ, ""); // invisible targ.takedamage = DAMAGE_NO; // No pain/death triggers targ.movetype = MOVETYPE_NONE; // Stationary targ.solid = SOLID_NOT; // no world interaction setsize (targ, VEC_ORIGIN, VEC_ORIGIN); // No size, no impact targ.velocity = targ.avelocity = '0 0 0'; // Frozen velocity targ.spawnflags = targ.effects = 0; // Leave flags alone targ.waitmin = time + LARGE_TIMER; // Block any touch function }; //---------------------------------------------------------------------- void(entity targ, float removetime) entity_remove = { if (targ == world) return; entity_hide(targ); targ.think = SUB_Remove; targ.nextthink = time + removetime; }; //====================================================================== // Show where broken monster/items are located (developer only) // SPNMARK_YELLOW, SPNMARK_BLUE, SPNMARK_GREEN, // SPNMARK_RED, SPNMARK_PURPLE, SPNMARK_WHITE //---------------------------------------------------------------------- void(vector marker_org, float marker_type) spawn_marker = { local entity brokend; if (developer > 0) { brokend = spawn(); brokend.classtype = CT_DEVMARKER; brokend.movetype = MOVETYPE_NONE; brokend.solid = SOLID_NOT; setmodel(brokend, MODEL_BROKEN); setorigin(brokend, marker_org); setsize (brokend, VEC_ORIGIN, VEC_ORIGIN); // Setup marker type (skin colour) // YELLOW = error, BLUE = delay, GREEN = nocount, RED = nightmare brokend.skin = marker_type; brokend.frame = 0; // If DP engine active remove particle shadow if (engine == ENG_DPEXT) brokend.effects = brokend.effects + EF_NOSHADOW; } }; //---------------------------------------------------------------------- entity(vector spawn_org) spawn_devmarker = { local entity spawn_ent; spawn_ent = spawn(); spawn_ent.classtype = CT_DEVMARKER; spawn_ent.movetype = MOVETYPE_NONE; spawn_ent.solid = SOLID_NOT; setmodel(spawn_ent, MODEL_BROKEN); setorigin(spawn_ent, spawn_org); setsize (spawn_ent, VEC_ORIGIN, VEC_ORIGIN); return spawn_ent; }; //---------------------------------------------------------------------- // Check for SKILL spawning conditions (frame 0 check ONLY) // Returns FALSE = do nothing TRUE = inhibit spawning //---------------------------------------------------------------------- float() check_nightmare = { // Check for nightmare skill only spawning if (self.nightmare == TRUE) { // Double check the coop console variable, not setup till frame 1+ // Use a nosave variable to prevent constant cvar command checking if (skill_cvar != TRUE) { skill = cvar("skill"); skill_cvar = TRUE; } if (skill == SKILL_NIGHTMARE) return FALSE; else { // Check for BSP entities has origin is different if (self.bsporigin) spawn_marker(bmodel_origin(self), SPNMARK_RED); else spawn_marker(self.origin, SPNMARK_RED); // Hide entity instead of removing it // Because of possible active touch functions entity_hide(self); return TRUE; } } return FALSE; }; //---------------------------------------------------------------------- // Check for COOP spawning conditions (frame 0 check ONLY) // Returns FALSE = do nothing TRUE = inhibit spawning //---------------------------------------------------------------------- float() check_coop = { // Check for coop only spawning conditions if (self.cooponly == TRUE || self.cooponly == NEGATIVE) { // Double check the coop console variable, not setup till frame 1+ // Use a nosave variable to prevent constant cvar command checking if (coop_cvar != TRUE) { coop = cvar("coop"); coop_cvar = TRUE; } // Only one condition will let a cooponly item spawn // cooponly = 1 coop = 0 inhibit = TRUE // cooponly = 1 coop = 1 inhibit = FALSE // cooponly = -1 coop = 0 inhibit = TRUE // cooponly = -1 coop = 1 inhibit = TRUE if (self.cooponly == TRUE && coop == TRUE) return FALSE; else { // Check for BSP entities has origin is different if (self.bsporigin) spawn_marker(bmodel_origin(self), SPNMARK_PURPLE); else spawn_marker(self.origin, SPNMARK_PURPLE); // Hide entity instead of removing it // Because of possible active touch functions entity_hide(self); return TRUE; } } return FALSE; }; //====================================================================== // Clear all trigger strings // //---------------------------------------------------------------------- void (entity targ) clear_trigstrs = { if (targ.target != "") targ.target = string_null; if (targ.target2 != "") targ.target2 = string_null; if (targ.killtarget != "") targ.killtarget = string_null; }; //====================================================================== // Vector randomizer, used mostly for avelocity setups // Used all over the place; monsters, weapons and gibs! // //---------------------------------------------------------------------- vector(float base, float rndmix, float plusminus) vecrand = { local vector vecmix; if (plusminus) { vecmix_x = base + crandom() * rndmix; vecmix_y = base + crandom() * rndmix; vecmix_z = base + crandom() * rndmix; } else { vecmix_x = base + random() * rndmix; vecmix_y = base + random() * rndmix; vecmix_z = base + random() * rndmix; } return vecmix; }; //====================================================================== // model_fade : Gradually fade out a model over time // * Optionally entity removal if height = 0 // //---------------------------------------------------------------------- void() model_fade = { self.alpha = 1 - ((time - self.ltime) / 1); if (self.alpha > 1) self.alpha = 1; else if (self.alpha <= 0) { self.alpha = 0; self.modelindex = 0; // Make sure no model self.model = ""; // Remove model by default if (!self.height) { self.think = SUB_Remove; self.nextthink = time + 1; } return; } self.nextthink = time + FADEMODEL_TIME; };