#pragma semicolon 1 #define PLUGIN_AUTHOR "Cloud Strife" #define PLUGIN_VERSION "1.0b" #include #include #pragma newdecls required bool enabled = false; ArrayList g_aMovingNpc = null; ArrayList g_aNpcConfigNT = null; StringMap g_mNpcConfig = null; public Plugin myinfo = { name = "MovingNPC vscripts", author = PLUGIN_AUTHOR, description = "MovingNPC vscripts", version = PLUGIN_VERSION, url = "https://steamcommunity.com/id/cloudstrifeua/" }; //TODO: Add start and stop triggers methodmap MovingNpcConfig < Basic { public MovingNpcConfig() { Basic myclass = new Basic(); myclass.SetFloat("fRate", 0.1); myclass.SetFloat("fDistance", 5000.0); myclass.SetFloat("fRetarget", 7.5); myclass.SetFloat("fForward", 1.0); myclass.SetFloat("fTurning", 0.5); myclass.SetFloat("fLifetime", 0.0); myclass.SetString("sThrusterFwd", ""); myclass.SetString("sThrusterSide", ""); myclass.SetString("sAttachment", ""); myclass.SetString("sTemplate", ""); return view_as(myclass); } property float lifetime { public get() { return this.GetFloat("fLifetime"); } public set(float val) { this.SetFloat("fLifetime", val); } } property float rate { public get() { return this.GetFloat("fRate"); } public set(float val) { this.SetFloat("fRate", val); } } property float distance { public get() { return this.GetFloat("fDistance"); } public set(float val) { this.SetFloat("fDistance", val); } } property float retarget { public get() { return this.GetFloat("fRetarget"); } public set(float val) { this.SetFloat("fRetarget", val); } } property float forward_factor { public get() { return this.GetFloat("fForward"); } public set(float val) { this.SetFloat("fForward", val); } } property float turning_factor { public get() { return this.GetFloat("fTurning"); } public set(float val) { this.SetFloat("fTurning", val); } } public int GetThrusterFwd(char[] buffer, int size) { return this.GetString("sThrusterFwd", buffer, size); } public void SetThrusterFwd(const char[] sThruster) { this.SetString("sThrusterFwd", sThruster); } public int GetThrusterSide(char[] buffer, int size) { return this.GetString("sThrusterSide", buffer, size); } public void SetThrusterSide(const char[] sThruster) { this.SetString("sThrusterSide", sThruster); } public int GetAttachment(char[] buffer, int size) { return this.GetString("sAttachment", buffer, size); } public void SetAttachment(const char[] sAttachment) { this.SetString("sAttachment", sAttachment); } public int GetTemplate(char[] buffer, int size) { return this.GetString("sTemplate", buffer, size); } public void SetTemplate(const char[] sTemplate) { this.SetString("sTemplate", sTemplate); } public void Delete() { delete this; } } stock int GetEntityIndex(int entity, const char[] name, const char[] classname = "*") { if (!enabled) { return 0; } if(name[0] == '#') { return Vscripts_GetEntityIndexByHammerID(StringToInt(name[1]), classname, entity); } else { return Vscripts_GetEntityIndexByName(name, classname, entity); } } public bool IsMovingNpcExists(int entity) { if (!enabled) { return false; } for(int i = 0; i < g_aMovingNpc.Length; ++i) { MovingNpc npc = g_aMovingNpc.Get(i); if(npc.entity == entity || npc.tf == entity || npc.ts == entity) return true; } return false; } public void KillNpc(MovingNpc npc) { if (!enabled) { return; } for(int i = 0; i < g_aMovingNpc.Length; ++i) { MovingNpc cur = g_aMovingNpc.Get(i); if(cur.entity == npc.entity) { npc.Stop(); g_aMovingNpc.Erase(i); break; } } npc.kill = true; } public Action OnMovingNpcTimeout(Handle timer, MovingNpc npc) { KillTimer(timer); npc.lifetimer = null; KillNpc(npc); return Plugin_Stop; } public void StartNpc(MovingNpc npc) { if (!enabled) { return; } npc.Start(); if(npc.lifetime > 0 && !npc.lifetimer) npc.lifetimer = CreateTimer(npc.lifetime, OnMovingNpcTimeout, npc, TIMER_FLAG_NO_MAPCHANGE); } public int GetEntName(int entity, char[] buffer, int size) { return GetEntPropString(entity, Prop_Data, "m_iName", buffer, size); } public int GetEntHammerID(int entity) { return GetEntProp(entity, Prop_Data, "m_iHammerID"); } stock bool MatchTrigger(int entity, const char[] sTrigger, bool namefixup = false) { if (!enabled) { return false; } if(sTrigger[0] == '#') { return StringToInt(sTrigger[1]) == GetEntHammerID(entity); } else { char name[MAX_ENT_NAME]; GetEntName(entity, name, sizeof(name)); if(!name[0]) return false; if(namefixup) { int c = FindCharInString(name, '&', true); if(c != -1) { name[c] = '\0'; } } return StrEqual(name, sTrigger); } } public void OnMapStart() { enabled = false; char sConfigPath[PLATFORM_MAX_PATH]; char sCurMap[PLATFORM_MAX_PATH]; GetCurrentMap(sCurMap, sizeof(sCurMap)); BuildPath(Path_SM, sConfigPath, sizeof(sConfigPath), "configs/movingnpc/%s.cfg", sCurMap); if(!FileExists(sConfigPath)) { return; } KeyValues Config = new KeyValues("npc"); if(!Config.ImportFromFile(sConfigPath)) { LogMessage("ImportFromFile() failed for map %s!", sCurMap); return; } Config.Rewind(); if(!Config.GotoFirstSubKey(true)) { LogMessage("The current map does not have any moving npcs configured."); return; } enabled = true; g_mNpcConfig = new StringMap(); g_aNpcConfigNT = new ArrayList(); do { MovingNpcConfig NpcConf = new MovingNpcConfig(); char buffer[MAX_ENT_NAME + MAX_INPUT_NAME]; Config.GetString("thruster_forward", buffer, sizeof(buffer), ""); if(!buffer[0]) { delete NpcConf; LogMessage("Could not find \"thruster_forward\" in config for map %s", sCurMap); continue; } NpcConf.SetThrusterFwd(buffer); Config.GetString("thruster_side", buffer, sizeof(buffer), ""); if(!buffer[0]) { delete NpcConf; LogMessage("Could not find \"thruster_side\" in config for map %s", sCurMap); continue; } NpcConf.SetThrusterSide(buffer); Config.GetString("attachment", buffer, sizeof(buffer), ""); if(!buffer[0]) { delete NpcConf; LogMessage("Could not find \"attachment\" in config for map %s", sCurMap); continue; } NpcConf.SetAttachment(buffer); char sTemplate[MAX_ENT_NAME]; Config.GetString("template", sTemplate, sizeof(sTemplate), ""); NpcConf.SetTemplate(sTemplate); NpcConf.rate = Config.GetFloat("tickrate", 0.1); NpcConf.distance = Config.GetFloat("distance", 5000.0); NpcConf.retarget = Config.GetFloat("retarget", 7.5); NpcConf.forward_factor = Config.GetFloat("forward_factor", 1.0); NpcConf.turning_factor = Config.GetFloat("turning_factor", 0.5); NpcConf.lifetime = Config.GetFloat("lifetime", 0.0); if(sTemplate[0]) { ArrayList tmp; if(!g_mNpcConfig.GetValue(sTemplate, tmp)) { tmp = new ArrayList(); } tmp.Push(NpcConf); g_mNpcConfig.SetValue(sTemplate, tmp, true); } else { g_aNpcConfigNT.Push(NpcConf); } } while(Config.GotoNextKey(true)); delete Config; g_aMovingNpc = new ArrayList(); HookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy); } public void OnRoundStart(Event event, const char[] name, bool dontBroadcast) { if (!enabled) { return; } for(int i = 0; i < g_aNpcConfigNT.Length; ++i) { MovingNpcConfig NpcConf = g_aNpcConfigNT.Get(i); char sThrusterFwd[MAX_ENT_NAME], sThrusterSide[MAX_ENT_NAME], sAttachment[MAX_ENT_NAME]; NpcConf.GetThrusterFwd(sThrusterFwd, sizeof(sThrusterFwd)); NpcConf.GetThrusterSide(sThrusterSide, sizeof(sThrusterSide)); NpcConf.GetAttachment(sAttachment, sizeof(sAttachment)); int thruster_fwd = -1, thruster_side = -1, attachment = -1; while((thruster_fwd = GetEntityIndex(thruster_fwd, sThrusterFwd, "phys_thruster")) != -1) { if(IsMovingNpcExists(thruster_fwd)) continue; do { thruster_side = GetEntityIndex(thruster_side, sThrusterSide, "phys_thruster"); } while (thruster_side != -1 && IsMovingNpcExists(thruster_side)); do { attachment = GetEntityIndex(attachment, sAttachment); } while (attachment != -1 && IsMovingNpcExists(attachment)); if(thruster_side != -1 && attachment != -1) { NewMovingNpc(NpcConf, attachment, thruster_fwd, thruster_side); } } } } stock void NewMovingNpc(MovingNpcConfig NpcConf, int attachment, int thruster_fwd, int thruster_side) { if (!enabled) { return; } MovingNpc npc = new MovingNpc(attachment, NpcConf.rate, NpcConf.distance, NpcConf.retarget, NpcConf.forward_factor, NpcConf.turning_factor, NpcConf.lifetime); npc.SetThruster(true, thruster_fwd); npc.SetThruster(false, thruster_side); StartNpc(npc); g_aMovingNpc.Push(npc); } public void Vscritps_OnTemplateInstanceCreated(int template, const int[] createdEntities, int size) { if (!enabled) { return; } if(!g_mNpcConfig) return; char sTemplate[MAX_ENT_NAME]; GetEntName(template, sTemplate, sizeof(sTemplate)); ArrayList configs; if(!g_mNpcConfig.GetValue(sTemplate, configs)) { Format(sTemplate, sizeof(sTemplate), "#%d", GetEntHammerID(template)); if(!g_mNpcConfig.GetValue(sTemplate, configs)) { return; } } for(int i = 0; i < configs.Length; ++i) { int attachment = -1, thruster_fwd = -1, thruster_side = -1; MovingNpcConfig npcConf = configs.Get(i); char sAttachment[MAX_ENT_NAME], sThrusterFwd[MAX_ENT_NAME], sThrusterSide[MAX_ENT_NAME]; npcConf.GetAttachment(sAttachment, sizeof(sAttachment)); npcConf.GetThrusterFwd(sThrusterFwd, sizeof(sThrusterFwd)); npcConf.GetThrusterSide(sThrusterSide, sizeof(sThrusterSide)); for(int e = 0; e < size; ++e) { int entity = createdEntities[e]; if(MatchTrigger(entity, sAttachment, true)) { attachment = entity; } else if(MatchTrigger(entity, sThrusterFwd, true)) { thruster_fwd = entity; } else if(MatchTrigger(entity, sThrusterSide, true)) { thruster_side = entity; } } if(attachment != -1 && thruster_fwd != -1 && thruster_side != -1) { NewMovingNpc(npcConf, attachment, thruster_fwd, thruster_side); break; } } } public void OnEntityDestroyed(int entity) { if (!enabled) { return; } if(!g_aMovingNpc) return; for (int i = 0; i < g_aMovingNpc.Length; ++i) { MovingNpc npc = g_aMovingNpc.Get(i); if(npc.entity == entity || npc.tf == entity || npc.ts == entity) { npc.Stop(); npc.kill = true; g_aMovingNpc.Erase(i); break; } } } public void OnMapEnd() { UnhookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy); if (!enabled) { return; } if(g_aMovingNpc) { for (int i = 0; i < g_aMovingNpc.Length; ++i) { MovingNpc npc = g_aMovingNpc.Get(i); npc.Stop(); npc.kill = true; } delete g_aMovingNpc; } if(g_aNpcConfigNT) { for (int i = 0; i < g_aNpcConfigNT.Length; ++i) { MovingNpcConfig NpcConf = g_aNpcConfigNT.Get(i); NpcConf.Delete(); } delete g_aNpcConfigNT; } if(g_mNpcConfig) { StringMapSnapshot ms = g_mNpcConfig.Snapshot(); char sTemplate[MAX_ENT_NAME]; for(int i = 0; i < ms.Length; ++i) { ms.GetKey(i, sTemplate, sizeof(sTemplate)); ArrayList configs; g_mNpcConfig.GetValue(sTemplate, configs); for(int c = 0; c < configs.Length; ++c) { MovingNpcConfig NpcConf = configs.Get(c); NpcConf.Delete(); } delete configs; } delete ms; g_mNpcConfig.Clear(); delete g_mNpcConfig; } }