From cb8d244030f0eef7adb55ae273d197447a365d13 Mon Sep 17 00:00:00 2001 From: jenz Date: Tue, 18 Aug 2026 15:02:18 +0200 Subject: [PATCH] simple feature through cvar to report when people shoot the couch --- PropSpawner/scripting/PropSpawner.sp | 92 +++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/PropSpawner/scripting/PropSpawner.sp b/PropSpawner/scripting/PropSpawner.sp index 582191d..df03f01 100644 --- a/PropSpawner/scripting/PropSpawner.sp +++ b/PropSpawner/scripting/PropSpawner.sp @@ -1,6 +1,7 @@ #pragma semicolon 1 #include #include +#include public Plugin myinfo = { @@ -19,6 +20,10 @@ Menu g_NpcMenu = null; KeyValues g_Models; +ConVar g_cvNotifyShot; + +ArrayList g_aSpawnedProps; // Tracks entity indices of props spawned by this plugin so we can unhook them + public void OnPluginStart() { @@ -42,6 +47,48 @@ public void OnPluginStart() g_Models.Rewind(); // Go back to the root node RegAdminCmd("sm_propspawner", Command_MainMenu, ADMFLAG_CHANGEMAP); //g + + g_cvNotifyShot = CreateConVar("sm_propspawner_notify_shot", "0", "If set to 1, announces in chat when a plugin-spawned prop is shot.", FCVAR_NOTIFY, true, 0.0, true, 1.0); + AutoExecConfig(true, "PropSpawner"); + + g_aSpawnedProps = new ArrayList(); + + HookEvent("round_end", Event_RoundEnd); +} + +public void OnEntityDestroyed(int entity) +{ + // Safety net: if a tracked prop is removed by any means other than DeleteProp() + // (e.g. an explosion, another plugin, map cleanup), stop tracking it here too. + if (entity <= 0 || g_aSpawnedProps == null) + return; + + int index = g_aSpawnedProps.FindValue(entity); + if (index != -1) + { + g_aSpawnedProps.Erase(index); + } +} + +public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + UnhookAllSpawnedProps(); +} + +void UnhookAllSpawnedProps() +{ + if (g_aSpawnedProps == null || !g_cvNotifyShot.BoolValue) + return; + + for (int i = 0; i < g_aSpawnedProps.Length; i++) + { + int prop = g_aSpawnedProps.Get(i); + if (IsValidEntity(prop)) + { + SDKUnhook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage); + } + } + g_aSpawnedProps.Clear(); } public void OnMapStart() @@ -82,6 +129,8 @@ public void OnMapEnd() delete(g_NpcMenu); g_NpcMenu = null; } + + UnhookAllSpawnedProps(); } /* ----------------------------------- */ @@ -177,6 +226,7 @@ public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2) } } } + return 0; } public RotationHandler(Menu menu, MenuAction action, int client, int param2) @@ -320,6 +370,15 @@ void DeleteProp(int client){ GetEdictClassname(DeleteIndex, classname, sizeof(classname)); if(isAProp(classname)) { + if (g_cvNotifyShot.BoolValue) + { + int propIndex = g_aSpawnedProps.FindValue(DeleteIndex); + if (propIndex != -1) + { + SDKUnhook(DeleteIndex, SDKHook_OnTakeDamage, OnPropTakeDamage); + g_aSpawnedProps.Erase(propIndex); + } + } AcceptEntityInput(DeleteIndex, "Kill", -1, -1, 0); ShowActivity2(client, "\x01[SM] \x04", "deleted a prop."); LogAction(client, -1, "\"%L\" deleted a prop.", client); @@ -405,7 +464,7 @@ void prop_any_create(int client) char sModelExplodeRadius[11]; int iModelExplode = g_Models.GetNum("explode",0); // if "explode" is not set, then it's 0 - g_Models.GetString("health", sModelHealth, sizeof(sModelHealth),"0"); // if "health" is not set, then it's 0 + g_Models.GetString("health", sModelHealth, sizeof(sModelHealth),"0"); // if "health" is not set, then its 0 g_Models.GetString("explodedamage", sModelExplodeDamage, sizeof(sModelExplodeDamage),"1"); g_Models.GetString("exploderadius", sModelExplodeRadius, sizeof(sModelExplodeRadius),"1"); @@ -484,7 +543,36 @@ void prop_any_create(int client) DispatchSpawn(prop); AcceptEntityInput(prop, "TurnOn", prop, prop, 0); AcceptEntityInput(prop, "EnableCollision"); + + // Hook the prop so we can detect when it gets shot/damaged, and track it so we can unhook later + if (g_cvNotifyShot.BoolValue) + { + SDKHook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage); + g_aSpawnedProps.Push(prop); + } ShowActivity2(client, "\x01[SM] \x04", "\x01Spawned \x04%s\x01.", sModelName); LogAction(client, -1, "\"%L\" Spawned \"%s\".", client, sModelName); -} \ No newline at end of file +} + +/* ----------------------------------- */ + +public Action OnPropTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom) +{ + if (!g_cvNotifyShot.BoolValue) + return Plugin_Continue; + + if (attacker < 1 || attacker > MaxClients || !IsClientInGame(attacker)) + return Plugin_Continue; + + char sPropName[64]; + GetEntPropString(victim, Prop_Data, "m_ModelName", sPropName, sizeof(sPropName)); + + char sAttackerName[MAX_NAME_LENGTH]; + GetClientName(attacker, sAttackerName, sizeof(sAttackerName)); + + PrintToChatAll("\x01[SM] \x04%s\x01 is shooting the couch.", sAttackerName); + + return Plugin_Continue; +} +