#pragma semicolon 1 #pragma newdecls required #include #include #include #include #include #include #include "CConfig.inc" #include "CBoss.inc" bool g_bLate = true; ArrayList g_aConfig; ArrayList g_aBoss; StringMap g_aHadOnce; Handle g_hFwd_OnBossInitialized; Handle g_hFwd_OnBossDamaged; Handle g_hFwd_OnBossKilled; ConVar g_hCvar_DebugMode; char g_sHUDText[256]; public Plugin myinfo = { name = "BossHP", author = "BotoX", description = "", version = "0.2", url = "" }; public void OnPluginStart() { g_hFwd_OnBossInitialized = CreateGlobalForward("OnBossInitialized", ET_Ignore, Param_Any, Param_Any); g_hFwd_OnBossDamaged = CreateGlobalForward("OnBossDamaged", ET_Ignore, Param_Any, Param_Any, Param_Cell, Param_Float); g_hFwd_OnBossKilled = CreateGlobalForward("OnBossKilled", ET_Ignore, Param_Any, Param_Any, Param_Cell); g_hCvar_DebugMode = CreateConVar("bosshp_debug", "0", _, _, true, 0.0, true, 1.0); HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); HookEntityOutput("env_entity_maker", "OnEntitySpawned", OnEnvEntityMakerEntitySpawned); } void LogDebugMessage(const char[] message, any ...) { if(g_hCvar_DebugMode.BoolValue) { char buffer[1024]; VFormat(buffer, sizeof(buffer), message, 2); LogMessage(buffer); } } public void OnPluginEnd() { Cleanup(); } void Cleanup() { if(g_aBoss) { for(int i = 0; i < g_aBoss.Length; i++) { CBoss Boss = g_aBoss.Get(i); Call_StartForward(g_hFwd_OnBossKilled); Call_PushCell(Boss); Call_PushCell(Boss.dConfig); Call_PushCell(0); Call_Finish(); delete Boss; } delete g_aBoss; } if(g_aConfig) { for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); delete Config; } delete g_aConfig; } } public void OnMapEnd() { Cleanup(); } public void OnMapStart() { bool bLate = g_bLate; g_bLate = false; char sMapName[PLATFORM_MAX_PATH]; GetCurrentMap(sMapName, sizeof(sMapName)); char sConfigFile[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/bosshp/%s.cfg", sMapName); if(!FileExists(sConfigFile)) { LogMessage("Could not find mapconfig: \"%s\"", sConfigFile); return; } LogMessage("Found mapconfig: \"%s\"", sConfigFile); KeyValues KvConfig = new KeyValues("bosses"); if(!KvConfig.ImportFromFile(sConfigFile)) { delete KvConfig; LogError("ImportFromFile() failed!"); return; } KvConfig.Rewind(); if(!KvConfig.GotoFirstSubKey()) { delete KvConfig; LogError("GotoFirstSubKey() failed!"); return; } g_aConfig = new ArrayList(); do { char sSection[64]; KvConfig.GetSectionName(sSection, sizeof(sSection)); char sName[64]; KvConfig.GetString("name", sName, sizeof(sName)); if(!sName[0]) { LogError("Could not find \"name\" in \"%s\"", sSection); continue; } char sMethod[64]; KvConfig.GetString("method", sMethod, sizeof(sMethod)); if(!sMethod[0]) { LogError("Could not find \"method\" in \"%s\"", sSection); continue; } char sTrigger[64 * 2]; KvConfig.GetString("trigger", sTrigger, sizeof(sTrigger)); if(!sTrigger[0]) { LogError("Could not find \"trigger\" in \"%s\"", sSection); continue; } int iTriggerDelim; if((iTriggerDelim = FindCharInString(sTrigger, ':')) == -1) { LogError("Delimiter ':' not found in \"trigger\"(%s) in \"%s\"", sTrigger, sSection); continue; } sTrigger[iTriggerDelim] = 0; float fTriggerDelay = 0.0; int iTriggerDelayDelim; if((iTriggerDelayDelim = FindCharInString(sTrigger[iTriggerDelim + 1], ':')) != -1) { iTriggerDelayDelim += iTriggerDelim + 1; fTriggerDelay = StringToFloat(sTrigger[iTriggerDelayDelim + 1]); sTrigger[iTriggerDelayDelim] = 0; } char sShowTrigger[64 * 2]; int iShowTriggerDelim; float fShowTriggerDelay = 0.0; int iShowTriggerDelayDelim; KvConfig.GetString("showtrigger", sShowTrigger, sizeof(sShowTrigger)); if(sShowTrigger[0]) { if((iShowTriggerDelim = FindCharInString(sShowTrigger, ':')) == -1) { LogError("Delimiter ':' not found in \"showtrigger\"(%s) in \"%s\"", sShowTrigger, sSection); continue; } sShowTrigger[iShowTriggerDelim] = 0; if((iShowTriggerDelayDelim = FindCharInString(sShowTrigger[iShowTriggerDelim + 1], ':')) != -1) { iShowTriggerDelayDelim += iShowTriggerDelim + 1; fShowTriggerDelay = StringToFloat(sShowTrigger[iShowTriggerDelayDelim + 1]); sShowTrigger[iShowTriggerDelayDelim] = 0; } } char sKillTrigger[64 * 2]; int iKillTriggerDelim; float fKillTriggerDelay = 0.0; int iKillTriggerDelayDelim; KvConfig.GetString("killtrigger", sKillTrigger, sizeof(sKillTrigger)); if(sKillTrigger[0]) { if((iKillTriggerDelim = FindCharInString(sKillTrigger, ':')) == -1) { LogError("Delimiter ':' not found in \"killtrigger\"(%s) in \"%s\"", sKillTrigger, sSection); continue; } sKillTrigger[iKillTriggerDelim] = 0; if((iKillTriggerDelayDelim = FindCharInString(sKillTrigger[iKillTriggerDelim + 1], ':')) != -1) { iKillTriggerDelayDelim += iKillTriggerDelim + 1; fKillTriggerDelay = StringToFloat(sKillTrigger[iKillTriggerDelayDelim + 1]); sKillTrigger[iKillTriggerDelayDelim] = 0; } } char sHurtTrigger[64 * 2]; int iHurtTriggerDelim; KvConfig.GetString("hurttrigger", sHurtTrigger, sizeof(sHurtTrigger)); if(sHurtTrigger[0]) { if((iHurtTriggerDelim = FindCharInString(sHurtTrigger, ':')) == -1) { LogError("Delimiter ':' not found in \"hurttrigger\"(%s) in \"%s\"", sHurtTrigger, sSection); continue; } sHurtTrigger[iHurtTriggerDelim] = 0; } bool bMultiTrigger = view_as(KvConfig.GetNum("multitrigger", 0)); bool bNameFixup = view_as(KvConfig.GetNum("namefixup", 0)); int iTimeout = KvConfig.GetNum("timeout", -1); CConfig Config = view_as(INVALID_HANDLE); if(StrEqual(sMethod, "breakable")) { char sBreakable[64]; if(!KvConfig.GetString("breakable", sBreakable, sizeof(sBreakable))) { LogError("Could not find \"breakable\" in \"%s\"", sSection); continue; } CConfigBreakable BreakableConfig = new CConfigBreakable(); BreakableConfig.SetBreakable(sBreakable); Config = view_as(BreakableConfig); } else if(StrEqual(sMethod, "counter")) { char sCounter[64]; if(!KvConfig.GetString("counter", sCounter, sizeof(sCounter))) { LogError("Could not find \"counter\" in \"%s\"", sSection); continue; } CConfigCounter CounterConfig = new CConfigCounter(); CounterConfig.SetCounter(sCounter); Config = view_as(CounterConfig); } else if(StrEqual(sMethod, "hpbar")) { char sIterator[64]; if(!KvConfig.GetString("iterator", sIterator, sizeof(sIterator))) { LogError("Could not find \"iterator\" in \"%s\"", sSection); continue; } char sCounter[64]; if(!KvConfig.GetString("counter", sCounter, sizeof(sCounter))) { LogError("Could not find \"counter\" in \"%s\"", sSection); continue; } char sBackup[64]; if(!KvConfig.GetString("backup", sBackup, sizeof(sBackup))) { LogError("Could not find \"backup\" in \"%s\"", sSection); continue; } CConfigHPBar HPBarConfig = new CConfigHPBar(); HPBarConfig.SetIterator(sIterator); HPBarConfig.SetCounter(sCounter); HPBarConfig.SetBackup(sBackup); Config = view_as(HPBarConfig); } if(Config == INVALID_HANDLE) { LogError("Invalid \"method\"(%s) in \"%s\"", sMethod, sSection); continue; } Config.SetName(sName); Config.bMultiTrigger = bMultiTrigger; Config.bNameFixup = bNameFixup; Config.iTimeout = iTimeout; Config.SetTrigger(sTrigger); Config.SetOutput(sTrigger[iTriggerDelim + 1]); Config.fTriggerDelay = fTriggerDelay; if(sShowTrigger[0]) { Config.SetShowTrigger(sShowTrigger); Config.SetShowOutput(sShowTrigger[iShowTriggerDelim + 1]); Config.fShowTriggerDelay = fShowTriggerDelay; } if(sKillTrigger[0]) { Config.SetKillTrigger(sKillTrigger); Config.SetKillOutput(sKillTrigger[iKillTriggerDelim + 1]); Config.fKillTriggerDelay = fKillTriggerDelay; } if(sHurtTrigger[0]) { Config.SetHurtTrigger(sHurtTrigger); Config.SetHurtOutput(sHurtTrigger[iHurtTriggerDelim + 1]); } g_aConfig.Push(Config); } while(KvConfig.GotoNextKey(false)); delete KvConfig; if(!g_aConfig.Length) { delete g_aConfig; LogError("Empty mapconfig: \"%s\"", sConfigFile); return; } g_aBoss = new ArrayList(); g_aHadOnce = new StringMap(); /* Late Load */ if(bLate) { int entity = INVALID_ENT_REFERENCE; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { OnEntitySpawned(entity); } } } public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { if(!g_aConfig) return; if(g_aBoss) { for(int i = 0; i < g_aBoss.Length; i++) { CBoss Boss = g_aBoss.Get(i); delete Boss; } delete g_aBoss; } g_aBoss = new ArrayList(); if(g_aHadOnce) delete g_aHadOnce; g_aHadOnce = new StringMap(); } public void OnEntityCreated(int entity, const char[] classname) { if(!g_aConfig) return; SDKHook(entity, SDKHook_SpawnPost, OnEntitySpawned); } public void OnEntitySpawned(int entity) { if(!g_aConfig) return; char sTargetname[64]; GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); char sTrigger[64]; Config.GetTrigger(sTrigger, sizeof(sTrigger)); int iTriggerHammerID = -1; if(sTrigger[0] == '#') iTriggerHammerID = StringToInt(sTrigger[1]); if((iTriggerHammerID == -1 && sTrigger[0] && StrEqual(sTargetname, sTrigger)) || iTriggerHammerID == iHammerID) { char sOutput[64]; Config.GetOutput(sOutput, sizeof(sOutput)); if(StrEqual(sOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePost); } else { bool Once = !Config.bMultiTrigger; HookSingleEntityOutput(entity, sOutput, OnEntityOutput, Once); } LogDebugMessage("Hooked trigger %s:%s", sTrigger, sOutput); } char sShowTrigger[64]; Config.GetShowTrigger(sShowTrigger, sizeof(sShowTrigger)); int iShowTriggerHammerID = -1; if(sShowTrigger[0] == '#') iShowTriggerHammerID = StringToInt(sShowTrigger[1]); if((iShowTriggerHammerID == -1 && sShowTrigger[0] && StrEqual(sTargetname, sShowTrigger)) || iShowTriggerHammerID == iHammerID) { char sShowOutput[64]; Config.GetShowOutput(sShowOutput, sizeof(sShowOutput)); if(StrEqual(sShowOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostShow); } else { bool Once = !Config.bMultiTrigger; HookSingleEntityOutput(entity, sShowOutput, OnEntityOutputShow, Once); } LogDebugMessage("Hooked showtrigger %s:%s", sShowTrigger, sShowOutput); } char sKillTrigger[64]; Config.GetKillTrigger(sKillTrigger, sizeof(sKillTrigger)); int iKillTriggerHammerID = -1; if(sKillTrigger[0] == '#') iKillTriggerHammerID = StringToInt(sKillTrigger[1]); if((iKillTriggerHammerID == -1 && sKillTrigger[0] && StrEqual(sTargetname, sKillTrigger)) || iKillTriggerHammerID == iHammerID) { char sKillOutput[64]; Config.GetKillOutput(sKillOutput, sizeof(sKillOutput)); if(StrEqual(sKillOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostKill); } else { bool Once = !Config.bMultiTrigger; HookSingleEntityOutput(entity, sKillOutput, OnEntityOutputKill, Once); } LogDebugMessage("Hooked killtrigger %s:%s", sKillTrigger, sKillOutput); } char sHurtTrigger[64]; Config.GetHurtTrigger(sHurtTrigger, sizeof(sHurtTrigger)); int iHurtTriggerHammerID = -1; if(sHurtTrigger[0] == '#') iHurtTriggerHammerID = StringToInt(sHurtTrigger[1]); if((iHurtTriggerHammerID == -1 && sHurtTrigger[0] && StrEqual(sTargetname, sHurtTrigger)) || iHurtTriggerHammerID == iHammerID) { char sHurtOutput[64]; Config.GetHurtOutput(sHurtOutput, sizeof(sHurtOutput)); if(StrEqual(sHurtOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostHurt); } else { HookSingleEntityOutput(entity, sHurtOutput, OnEntityOutputHurt); } LogDebugMessage("Hooked hurttrigger %s:%s", sHurtTrigger, sHurtOutput); } } } void OnTrigger(int entity, const char[] output, SDKHookType HookType = view_as(-1)) { char sTargetname[64]; GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); // PrintToServer("OnTrigger(%d:\"%s\":#%d, \"%s\")", entity, sTargetname, iHammerID, output); for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); char sTrigger[64]; Config.GetTrigger(sTrigger, sizeof(sTrigger)); int iTriggerHammerID = -1; if(sTrigger[0] == '#') { iTriggerHammerID = StringToInt(sTrigger[1]); if(iTriggerHammerID != iHammerID) continue; } else if(!sTargetname[0] || !StrEqual(sTargetname, sTrigger)) continue; char sOutput[64]; Config.GetOutput(sOutput, sizeof(sOutput)); if(!StrEqual(output, sOutput)) continue; bool Once = !Config.bMultiTrigger; char sTemp[8]; if(Once) { IntToString(i, sTemp, sizeof(sTemp)); bool bHadOnce = false; if(g_aHadOnce.GetValue(sTemp, bHadOnce) && bHadOnce) continue; } if(HookType != view_as(-1) && Once) { if(HookType == SDKHook_OnTakeDamagePost) SDKUnhook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePost); } CBoss Boss = view_as(INVALID_HANDLE); if(Config.IsBreakable) Boss = new CBossBreakable(); else if(Config.IsCounter) Boss = new CBossCounter(); else if(Config.IsHPBar) Boss = new CBossHPBar(); if(Boss != INVALID_HANDLE) { Boss.dConfig = Config; Boss.bActive = false; float fTriggerDelay = Config.fTriggerDelay; if(fTriggerDelay > 0) Boss.fWaitUntil = GetGameTime() + fTriggerDelay; char sShowTrigger[8]; Config.GetShowTrigger(sShowTrigger, sizeof(sShowTrigger)); if(sShowTrigger[0]) Boss.bShow = false; g_aBoss.Push(Boss); if(Once) g_aHadOnce.SetValue(sTemp, true); if(iTriggerHammerID == -1) LogDebugMessage("Triggered boss %s(%d) from output %s", sTargetname, entity, output); else LogDebugMessage("Triggered boss #%d(%d) from output %s", iTriggerHammerID, entity, output); } } } void OnShowTrigger(int entity, const char[] output, SDKHookType HookType = view_as(-1)) { char sTargetname[64]; GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); int iTemplateNum = -1; int iTemplateLoc = FindCharInString(sTargetname, '&', true); if(iTemplateLoc != -1) { iTemplateNum = StringToInt(sTargetname[iTemplateLoc + 1]); sTargetname[iTemplateLoc] = 0; } for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); char sShowTrigger[64]; Config.GetShowTrigger(sShowTrigger, sizeof(sShowTrigger)); if(!sShowTrigger[0]) continue; int iShowTriggerHammerID = -1; if(sShowTrigger[0] == '#') { iShowTriggerHammerID = StringToInt(sShowTrigger[1]); if(iShowTriggerHammerID != iHammerID) continue; } else if(!sTargetname[0] || !StrEqual(sTargetname, sShowTrigger)) continue; char sShowOutput[64]; Config.GetShowOutput(sShowOutput, sizeof(sShowOutput)); if(!StrEqual(output, sShowOutput)) continue; if(iShowTriggerHammerID == -1) LogDebugMessage("Triggered show boss %s(%d) from output %s", sTargetname, entity, output); else LogDebugMessage("Triggered show boss #%d(%d) from output %s", iShowTriggerHammerID, entity, output); if(HookType != view_as(-1) && !Config.bMultiTrigger) { if(HookType == SDKHook_OnTakeDamagePost) SDKUnhook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostShow); } float fShowTriggerDelay = Config.fShowTriggerDelay; for(int j = 0; j < g_aBoss.Length; j++) { CBoss Boss = g_aBoss.Get(j); if(Boss.dConfig != Config) continue; if(Boss.iTemplateNum != iTemplateNum) continue; if(fShowTriggerDelay > 0) { Boss.fShowAt = GetGameTime() + fShowTriggerDelay; LogDebugMessage("Scheduled show(%f) boss %d", fShowTriggerDelay, j); } else { Boss.bShow = true; LogDebugMessage("Showing boss %d", j); } } } } void OnKillTrigger(int entity, const char[] output, SDKHookType HookType = view_as(-1)) { char sTargetname[64]; GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); int iTemplateNum = -1; int iTemplateLoc = FindCharInString(sTargetname, '&', true); if(iTemplateLoc != -1) { iTemplateNum = StringToInt(sTargetname[iTemplateLoc + 1]); sTargetname[iTemplateLoc] = 0; } for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); char sKillTrigger[64]; Config.GetKillTrigger(sKillTrigger, sizeof(sKillTrigger)); if(!sKillTrigger[0]) continue; int iKillTriggerHammerID = -1; if(sKillTrigger[0] == '#') { iKillTriggerHammerID = StringToInt(sKillTrigger[1]); if(iKillTriggerHammerID != iHammerID) continue; } else if(!sTargetname[0] || !StrEqual(sTargetname, sKillTrigger)) continue; char sKillOutput[64]; Config.GetKillOutput(sKillOutput, sizeof(sKillOutput)); if(!StrEqual(output, sKillOutput)) continue; if(iKillTriggerHammerID == -1) LogDebugMessage("Triggered kill boss %s(%d) from output %s", sTargetname, entity, output); else LogDebugMessage("Triggered kill boss #%d(%d) from output %s", iKillTriggerHammerID, entity, output); if(HookType != view_as(-1) && !Config.bMultiTrigger) { if(HookType == SDKHook_OnTakeDamagePost) SDKUnhook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostKill); } float fKillTriggerDelay = Config.fKillTriggerDelay; for(int j = 0; j < g_aBoss.Length; j++) { CBoss Boss = g_aBoss.Get(j); if(Boss.dConfig != Config) continue; if(Boss.iTemplateNum != iTemplateNum) continue; if(fKillTriggerDelay > 0) { Boss.fKillAt = GetGameTime() + fKillTriggerDelay; LogDebugMessage("Scheduled kill(%f) boss %d", fKillTriggerDelay, j); } else { LogDebugMessage("Killed boss %d", j); Call_StartForward(g_hFwd_OnBossKilled); Call_PushCell(Boss); Call_PushCell(Boss.dConfig); Call_PushCell(1); Call_Finish(); delete Boss; g_aBoss.Erase(j); j--; } } } } void OnHurtTrigger(int entity, const char[] output, int activator, float damage = 1.0) { char sTargetname[64]; GetEntPropString(entity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); int iHammerID = GetEntProp(entity, Prop_Data, "m_iHammerID"); int iTemplateNum = -1; int iTemplateLoc = FindCharInString(sTargetname, '&', true); if(iTemplateLoc != -1) { iTemplateNum = StringToInt(sTargetname[iTemplateLoc + 1]); sTargetname[iTemplateLoc] = 0; } for(int i = 0; i < g_aConfig.Length; i++) { CConfig Config = g_aConfig.Get(i); char sHurtTrigger[64]; Config.GetHurtTrigger(sHurtTrigger, sizeof(sHurtTrigger)); if(!sHurtTrigger[0]) continue; int iHurtTriggerHammerID = -1; if(sHurtTrigger[0] == '#') { iHurtTriggerHammerID = StringToInt(sHurtTrigger[1]); if(iHurtTriggerHammerID != iHammerID) continue; } else if(!sTargetname[0] || !StrEqual(sTargetname, sHurtTrigger)) continue; char sHurtOutput[64]; Config.GetHurtOutput(sHurtOutput, sizeof(sHurtOutput)); if(!StrEqual(output, sHurtOutput)) continue; if(iHurtTriggerHammerID == -1) LogDebugMessage("Triggered hurt boss %s(%d) from output %s", sTargetname, entity, output); else LogDebugMessage("Triggered hurt boss #%d(%d) from output %s", iHurtTriggerHammerID, entity, output); for(int j = 0; j < g_aBoss.Length; j++) { CBoss Boss = g_aBoss.Get(j); if(Boss.dConfig != Config) continue; if(Boss.iTemplateNum != iTemplateNum) continue; Call_StartForward(g_hFwd_OnBossDamaged); Call_PushCell(Boss); Call_PushCell(Boss.dConfig); Call_PushCell(activator); Call_PushFloat(damage); Call_Finish(); } } } public void OnEnvEntityMakerEntitySpawned(const char[] output, int caller, int activator, float delay) { if(!g_aConfig) return; char sClassname[64]; if(!GetEntityClassname(caller, sClassname, sizeof(sClassname))) return; if(!StrEqual(sClassname, "env_entity_maker")) { LogError("[SOURCEMOD BUG] output: \"%s\", caller: %d, activator: %d, delay: %f, classname: \"%s\"", output, caller, activator, delay, sClassname); return; } char sPointTemplate[64]; if(GetEntPropString(caller, Prop_Data, "m_iszTemplate", sPointTemplate, sizeof(sPointTemplate)) <= 0) return; int iPointTemplate = FindEntityByTargetname(INVALID_ENT_REFERENCE, sPointTemplate, "point_template"); if(iPointTemplate == INVALID_ENT_REFERENCE) return; OnEntityOutput("OnEntitySpawned", iPointTemplate, caller, delay); } public void OnEntityOutput(const char[] output, int caller, int activator, float delay) { OnTrigger(caller, output); } public void OnEntityOutputShow(const char[] output, int caller, int activator, float delay) { OnShowTrigger(caller, output); } public void OnEntityOutputKill(const char[] output, int caller, int activator, float delay) { OnKillTrigger(caller, output); } public void OnEntityOutputHurt(const char[] output, int caller, int activator, float delay) { OnHurtTrigger(caller, output, activator); } public void OnTakeDamagePost(int victim, int attacker, int inflictor, float damage, int damagetype) { OnTrigger(victim, "OnTakeDamage", SDKHook_OnTakeDamagePost); } public void OnTakeDamagePostShow(int victim, int attacker, int inflictor, float damage, int damagetype) { OnShowTrigger(victim, "OnTakeDamage", SDKHook_OnTakeDamagePost); } public void OnTakeDamagePostKill(int victim, int attacker, int inflictor, float damage, int damagetype) { OnKillTrigger(victim, "OnTakeDamage", SDKHook_OnTakeDamagePost); } public void OnTakeDamagePostHurt(int victim, int attacker, int inflictor, float damage, int damagetype) { OnHurtTrigger(victim, "OnTakeDamage", attacker, damage); } public void OnGameFrame() { if(!g_aBoss) return; static bool s_bLastHudPrinted = false; g_sHUDText[0] = 0; for(int i = 0; i < g_aBoss.Length; i++) { CBoss Boss = g_aBoss.Get(i); if(Boss.fKillAt && Boss.fKillAt < GetGameTime()) { // Delete Boss LogDebugMessage("Deleting boss %d (KillAt)", i); Call_StartForward(g_hFwd_OnBossKilled); Call_PushCell(Boss); Call_PushCell(Boss.dConfig); Call_PushCell(1); Call_Finish(); delete Boss; g_aBoss.Erase(i); i--; continue; } if(!Boss.bActive) { if(Boss.fWaitUntil) { if(Boss.fWaitUntil > GetGameTime()) continue; Boss.fWaitUntil = 0.0; } if(!BossInit(Boss)) continue; } if(!Boss.bProcessing) { // Mark boss as processing, this will stay true on errors, preventing spam due to OnGameFrame otherwise constantly trying again. Boss.bProcessing = true; if(!BossProcess(Boss)) { // Delete Boss LogDebugMessage("Deleting boss %d (dead)", i); Call_StartForward(g_hFwd_OnBossKilled); Call_PushCell(Boss); Call_PushCell(Boss.dConfig); Call_PushCell(2); Call_Finish(); delete Boss; g_aBoss.Erase(i); i--; continue; } // Unmark Boss as processing. Boss.bProcessing = false; } } if(!IsVoteInProgress()) { if(g_sHUDText[0]) { for(int client = 1; client <= MaxClients; client++) { if(IsClientInGame(client)) { PrintHintText(client, g_sHUDText); StopSound(client, SNDCHAN_STATIC, "UI/hint.wav"); } } s_bLastHudPrinted = true; } else if(s_bLastHudPrinted) { for(int client = 1; client <= MaxClients; client++) { if(IsClientInGame(client)) { PrintHintText(client, ""); StopSound(client, SNDCHAN_STATIC, "UI/hint.wav"); } } s_bLastHudPrinted = false; } } } bool BossInit(CBoss _Boss) { CConfig _Config = _Boss.dConfig; bool bNameFixup = _Config.bNameFixup; int iTemplateNum = -1; if(_Boss.IsBreakable) { CBossBreakable Boss = view_as(_Boss); CConfigBreakable Config = view_as(_Config); char sBreakable[64]; Config.GetBreakable(sBreakable, sizeof(sBreakable)); int iBreakableEnt = INVALID_ENT_REFERENCE; if(!bNameFixup) { iBreakableEnt = FindEntityByTargetname(iBreakableEnt, sBreakable, "*"); if(iBreakableEnt == INVALID_ENT_REFERENCE) return false; } else { StrCat(sBreakable, sizeof(sBreakable), "&*"); while((iBreakableEnt = FindEntityByTargetname(iBreakableEnt, sBreakable, "*")) != INVALID_ENT_REFERENCE) { bool bSkip = false; for(int i = 0; i < g_aBoss.Length; i++) { CBoss _tBoss = g_aBoss.Get(i); if(!_tBoss.IsBreakable) continue; CBossBreakable tBoss = view_as(_tBoss); if(tBoss.iBreakableEnt == iBreakableEnt) { bSkip = true; break; } } if(!bSkip) break; } if(iBreakableEnt == INVALID_ENT_REFERENCE) return false; GetEntPropString(iBreakableEnt, Prop_Data, "m_iName", sBreakable, sizeof(sBreakable)); int iTemplateLoc = FindCharInString(sBreakable, '&', true); if(iTemplateLoc == -1) return false; iTemplateNum = StringToInt(sBreakable[iTemplateLoc + 1]); } Boss.iBreakableEnt = iBreakableEnt; } else if(_Boss.IsCounter) { CBossCounter Boss = view_as(_Boss); CConfigCounter Config = view_as(_Config); char sCounter[64]; Config.GetCounter(sCounter, sizeof(sCounter)); int iCounterEnt = INVALID_ENT_REFERENCE; if(!bNameFixup) { iCounterEnt = FindEntityByTargetname(iCounterEnt, sCounter, "math_counter"); if(iCounterEnt == INVALID_ENT_REFERENCE) return false; } else { StrCat(sCounter, sizeof(sCounter), "&*"); while((iCounterEnt = FindEntityByTargetname(iCounterEnt, sCounter, "math_counter")) != INVALID_ENT_REFERENCE) { char sBuf[64]; GetEntPropString(iCounterEnt, Prop_Data, "m_iName", sBuf, sizeof(sBuf)); bool bSkip = false; for(int i = 0; i < g_aBoss.Length; i++) { CBoss _tBoss = g_aBoss.Get(i); if(!_tBoss.IsCounter) continue; CBossCounter tBoss = view_as(_tBoss); if(tBoss.iCounterEnt == iCounterEnt) { bSkip = true; break; } } if(!bSkip) break; } if(iCounterEnt == INVALID_ENT_REFERENCE) return false; GetEntPropString(iCounterEnt, Prop_Data, "m_iName", sCounter, sizeof(sCounter)); int iTemplateLoc = FindCharInString(sCounter, '&', true); if(iTemplateLoc == -1) return false; iTemplateNum = StringToInt(sCounter[iTemplateLoc + 1]); } Boss.iCounterEnt = iCounterEnt; int iCounterOnHitMinCount = GetOutputCount(iCounterEnt, "m_OnHitMin"); int iCounterOnHitMaxCount = GetOutputCount(iCounterEnt, "m_OnHitMax"); if(iCounterOnHitMinCount == iCounterOnHitMaxCount) { int iMinDiff = RoundFloat(GetOutputValueFloat(iCounterEnt, "m_OutValue") - GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMin")); int iMaxDiff = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMax") - GetOutputValueFloat(iCounterEnt, "m_OutValue")); Config.bCounterReverse = iMaxDiff > iMinDiff; } else Config.bCounterReverse = iCounterOnHitMaxCount > iCounterOnHitMinCount; } else if(_Boss.IsHPBar) { CBossHPBar Boss = view_as(_Boss); CConfigHPBar Config = view_as(_Config); char sIterator[64]; char sCounter[64]; char sBackup[64]; Config.GetIterator(sIterator, sizeof(sIterator)); Config.GetCounter(sCounter, sizeof(sCounter)); Config.GetBackup(sBackup, sizeof(sBackup)); int iIteratorEnt = INVALID_ENT_REFERENCE; int iCounterEnt = INVALID_ENT_REFERENCE; int iBackupEnt = INVALID_ENT_REFERENCE; if(!bNameFixup) { iIteratorEnt = FindEntityByTargetname(iIteratorEnt, sIterator, "math_counter"); if(iIteratorEnt == INVALID_ENT_REFERENCE) return false; iCounterEnt = FindEntityByTargetname(iCounterEnt, sCounter, "math_counter"); if(iCounterEnt == INVALID_ENT_REFERENCE) return false; iBackupEnt = FindEntityByTargetname(iBackupEnt, sBackup, "math_counter"); if(iBackupEnt == INVALID_ENT_REFERENCE) return false; } else { StrCat(sIterator, sizeof(sIterator), "&*"); while((iIteratorEnt = FindEntityByTargetname(iIteratorEnt, sIterator, "math_counter")) != INVALID_ENT_REFERENCE) { bool bSkip = false; for(int i = 0; i < g_aBoss.Length; i++) { CBoss _tBoss = g_aBoss.Get(i); if(!_tBoss.IsHPBar) continue; CBossHPBar tBoss = view_as(_tBoss); if(tBoss.iIteratorEnt == iIteratorEnt) { bSkip = true; break; } } if(!bSkip) break; } if(iIteratorEnt == INVALID_ENT_REFERENCE) return false; GetEntPropString(iIteratorEnt, Prop_Data, "m_iName", sIterator, sizeof(sIterator)); int iTemplateLoc = FindCharInString(sIterator, '&', true); if(iTemplateLoc == -1) return false; StrCat(sCounter, sizeof(sCounter), sIterator[iTemplateLoc]); StrCat(sBackup, sizeof(sBackup), sIterator[iTemplateLoc]); iCounterEnt = FindEntityByTargetname(iCounterEnt, sCounter, "math_counter"); if(iCounterEnt == INVALID_ENT_REFERENCE) return false; iBackupEnt = FindEntityByTargetname(iBackupEnt, sBackup, "math_counter"); if(iBackupEnt == INVALID_ENT_REFERENCE) return false; iTemplateNum = StringToInt(sIterator[iTemplateLoc + 1]); } Boss.iIteratorEnt = iIteratorEnt; Boss.iCounterEnt = iCounterEnt; Boss.iBackupEnt = iBackupEnt; int iIteratorOnHitMinCount = GetOutputCount(iIteratorEnt, "m_OnHitMin"); int iIteratorOnHitMaxCount = GetOutputCount(iIteratorEnt, "m_OnHitMax"); if(iIteratorOnHitMinCount == iIteratorOnHitMaxCount) { int iMinDiff = RoundFloat(GetOutputValueFloat(iIteratorEnt, "m_OutValue") - GetEntPropFloat(iIteratorEnt, Prop_Data, "m_flMin")); int iMaxDiff = RoundFloat(GetEntPropFloat(iIteratorEnt, Prop_Data, "m_flMax") - GetOutputValueFloat(iIteratorEnt, "m_OutValue")); Config.bIteratorReverse = iMaxDiff > iMinDiff; } else Config.bIteratorReverse = iIteratorOnHitMaxCount > iIteratorOnHitMinCount; int iCounterOnHitMinCount = GetOutputCount(iCounterEnt, "m_OnHitMin"); int iCounterOnHitMaxCount = GetOutputCount(iCounterEnt, "m_OnHitMax"); if(iCounterOnHitMinCount == iCounterOnHitMaxCount) { int iMinDiff = RoundFloat(GetOutputValueFloat(iCounterEnt, "m_OutValue") - GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMin")); int iMaxDiff = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMax") - GetOutputValueFloat(iCounterEnt, "m_OutValue")); Config.bCounterReverse = iMaxDiff > iMinDiff; } else Config.bCounterReverse = iCounterOnHitMaxCount > iCounterOnHitMinCount; } _Boss.bActive = true; if(iTemplateNum != -1) { _Boss.iTemplateNum = iTemplateNum; char sShowTrigger[64]; _Config.GetShowTrigger(sShowTrigger, sizeof(sShowTrigger)); if(sShowTrigger[0]) { Format(sShowTrigger, sizeof(sShowTrigger), "%s&%04d", sShowTrigger, iTemplateNum); char sShowOutput[64]; _Config.GetShowOutput(sShowOutput, sizeof(sShowOutput)); int entity = INVALID_ENT_REFERENCE; while((entity = FindEntityByTargetname(entity, sShowTrigger)) != INVALID_ENT_REFERENCE) { if(StrEqual(sShowOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostShow); } else { bool Once = !_Config.bMultiTrigger; HookSingleEntityOutput(entity, sShowOutput, OnEntityOutputShow, Once); } LogDebugMessage("Hooked showtrigger %s:%s", sShowTrigger, sShowOutput); } } char sKillTrigger[64]; _Config.GetKillTrigger(sKillTrigger, sizeof(sKillTrigger)); if(sKillTrigger[0]) { Format(sKillTrigger, sizeof(sKillTrigger), "%s&%04d", sKillTrigger, iTemplateNum); char sKillOutput[64]; _Config.GetKillOutput(sKillOutput, sizeof(sKillOutput)); int entity = INVALID_ENT_REFERENCE; while((entity = FindEntityByTargetname(entity, sKillTrigger)) != INVALID_ENT_REFERENCE) { if(StrEqual(sKillOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostKill); } else { bool Once = !_Config.bMultiTrigger; HookSingleEntityOutput(entity, sKillOutput, OnEntityOutputKill, Once); } LogDebugMessage("Hooked killtrigger %s:%s", sKillTrigger, sKillOutput); } } char sHurtTrigger[64]; _Config.GetHurtTrigger(sHurtTrigger, sizeof(sHurtTrigger)); if(sHurtTrigger[0]) { Format(sHurtTrigger, sizeof(sHurtTrigger), "%s&%04d", sHurtTrigger, iTemplateNum); char sHurtOutput[64]; _Config.GetHurtOutput(sHurtOutput, sizeof(sHurtOutput)); int entity = INVALID_ENT_REFERENCE; while((entity = FindEntityByTargetname(entity, sHurtTrigger)) != INVALID_ENT_REFERENCE) { if(StrEqual(sHurtOutput, "OnTakeDamage")) { SDKHook(entity, SDKHook_OnTakeDamagePost, OnTakeDamagePostHurt); } else { HookSingleEntityOutput(entity, sHurtOutput, OnEntityOutputHurt); } LogDebugMessage("Hooked hurttrigger %s:%s", sHurtTrigger, sHurtOutput); } } } char sBoss[64]; _Config.GetName(sBoss, sizeof(sBoss)); LogDebugMessage("Initialized boss %s (template = %d)", sBoss, iTemplateNum); Call_StartForward(g_hFwd_OnBossInitialized); Call_PushCell(_Boss); Call_PushCell(_Boss.dConfig); Call_Finish(); return true; } bool BossProcess(CBoss _Boss) { CConfig _Config = _Boss.dConfig; bool bInvalid = false; int iHealth = 0; int iLastHealth = _Boss.iHealth; int iLastChange = _Boss.iLastChange; if(_Boss.IsBreakable) { CBossBreakable Boss = view_as(_Boss); int iBreakableEnt = Boss.iBreakableEnt; if(IsValidEntity(iBreakableEnt)) iHealth = GetEntProp(iBreakableEnt, Prop_Data, "m_iHealth"); else bInvalid = true; } else if(_Boss.IsCounter) { CBossCounter Boss = view_as(_Boss); CConfigCounter Config = view_as(_Config); int iCounterEnt = Boss.iCounterEnt; if(IsValidEntity(iCounterEnt)) { int iCounterVal = RoundFloat(GetOutputValueFloat(iCounterEnt, "m_OutValue")); if(!Config.bCounterReverse) { int iCounterMin = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMin")); iHealth = iCounterVal - iCounterMin; } else { int iCounterMax = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMax")); iHealth = iCounterMax - iCounterVal; } } else bInvalid = true; } else if(_Boss.IsHPBar) { CBossHPBar Boss = view_as(_Boss); CConfigHPBar Config = view_as(_Config); int iIteratorEnt = Boss.iIteratorEnt; int iCounterEnt = Boss.iCounterEnt; int iBackupEnt = Boss.iBackupEnt; if(IsValidEntity(iIteratorEnt) && IsValidEntity(iCounterEnt) && IsValidEntity(iBackupEnt)) { int iIteratorVal = RoundFloat(GetOutputValueFloat(iIteratorEnt, "m_OutValue")); int iCounterVal = RoundFloat(GetOutputValueFloat(iCounterEnt, "m_OutValue")); int iBackupVal = RoundFloat(GetOutputValueFloat(iBackupEnt, "m_OutValue")); if(!Config.bIteratorReverse) { int iIteratorMin = RoundFloat(GetEntPropFloat(iIteratorEnt, Prop_Data, "m_flMin")); iHealth = (iIteratorVal - iIteratorMin - 1) * iBackupVal; } else { int iIteratorMax = RoundFloat(GetEntPropFloat(iIteratorEnt, Prop_Data, "m_flMax")); iHealth = (iIteratorMax - iIteratorVal - 1) * iBackupVal; } if(!Config.bCounterReverse) { int iCounterMin = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMin")); iHealth += iCounterVal - iCounterMin; } else { int iCounterMax = RoundFloat(GetEntPropFloat(iCounterEnt, Prop_Data, "m_flMax")); iHealth += iCounterMax - iCounterVal; } } else bInvalid = true; } if(iHealth < 0) iHealth = 0; if(iHealth != iLastHealth) { iLastChange = GetTime(); _Boss.iLastChange = iLastChange; } // Boss hasn't initialized HP yet. if(iHealth == 0 && iLastHealth == 0) { // Boss invalid: Delete boss if(bInvalid) return false; return true; } _Boss.iHealth = iHealth; bool bShow = _Boss.bShow; if(!bShow && _Boss.fShowAt && _Boss.fShowAt < GetGameTime()) { bShow = true; _Boss.bShow = true; } if(bShow) { int iTimeout = _Config.iTimeout; if(iTimeout < 0 || GetTime() - iLastChange < iTimeout) { char sFormat[64]; if(g_sHUDText[0]) { sFormat[0] = '\n'; _Config.GetName(sFormat[1], sizeof(sFormat) - 1); } else _Config.GetName(sFormat, sizeof(sFormat)); int FormatLen = strlen(sFormat); sFormat[FormatLen++] = ':'; sFormat[FormatLen++] = ' '; FormatLen += IntToString(iHealth, sFormat[FormatLen], sizeof(sFormat) - FormatLen); sFormat[FormatLen] = 0; StrCat(g_sHUDText, sizeof(g_sHUDText), sFormat); } } // Boss dead/invalid: Delete boss if(!iHealth || bInvalid) return false; return true; } int FindEntityByTargetname(int entity, const char[] sTargetname, const char[] sClassname="*") { if(sTargetname[0] == '#') // HammerID { int HammerID = StringToInt(sTargetname[1]); while((entity = FindEntityByClassname(entity, sClassname)) != INVALID_ENT_REFERENCE) { if(GetEntProp(entity, Prop_Data, "m_iHammerID") == HammerID) return entity; } } else // Targetname { int Wildcard = FindCharInString(sTargetname, '*'); char sTargetnameBuf[64]; while((entity = FindEntityByClassname(entity, sClassname)) != INVALID_ENT_REFERENCE) { if(GetEntPropString(entity, Prop_Data, "m_iName", sTargetnameBuf, sizeof(sTargetnameBuf)) <= 0) continue; if(strncmp(sTargetnameBuf, sTargetname, Wildcard) == 0) return entity; } } return INVALID_ENT_REFERENCE; }