diff --git a/prophealth/cl_plugin.lua b/prophealth/cl_plugin.lua new file mode 100644 index 0000000..c98ac3d --- /dev/null +++ b/prophealth/cl_plugin.lua @@ -0,0 +1,51 @@ +local PLUGIN = PLUGIN + +local math_floor = math.floor +local math_max = math.max + +local function PropHealthThink(row) + local entity = row.ixEntity + local tooltip = row.ixTooltip + + if (not IsValid(entity)) then return end + + -- Fetch vars + local hp = math_max(0, entity:GetNetVar("propHP", 0)) + local maxHP = entity:GetNetVar("propMaxHP", 100) + + if (row.ixLastHP == hp and row.ixLastMax == maxHP) then return end + + -- Update cache + row.ixLastHP = hp + row.ixLastMax = maxHP + + -- Update UI + local text = L("propHealth") .. ": " .. math_floor(hp) .. " / " .. math_floor(maxHP) + row:SetText(text) + row:SizeToContents() + + if (tooltip) then tooltip:SizeToContents() end +end + +function PLUGIN:PopulateEntityInfo(entity, tooltip) + -- Check class string first (fastest check) + if (entity:GetClass() ~= "prop_physics") then return end + + -- Check NetVar existence + if (not entity:GetNetVar("propHP")) then return end + + local row = tooltip:AddRow("health") + row:SetBackgroundColor(Color(200, 50, 50)) + + -- Store references on the panel object itself + row.ixEntity = entity + row.ixTooltip = tooltip + + -- Initialize cache variables + row.ixLastHP = -1 + row.ixLastMax = -1 + + -- Assign the static Think function (Zero allocation) + row.Think = PropHealthThink + PropHealthThink(row) +end \ No newline at end of file diff --git a/prophealth/sh_plugin.lua b/prophealth/sh_plugin.lua new file mode 100644 index 0000000..326d591 --- /dev/null +++ b/prophealth/sh_plugin.lua @@ -0,0 +1,75 @@ +local PLUGIN = PLUGIN + +PLUGIN.name = "Prop Health" +PLUGIN.author = "Max_auCube" +PLUGIN.description = "Add HP to props." + +PLUGIN.license = [[ + +MIT License + +Copyright (c) 2026 Max_auCube + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +]] + +PLUGIN.readme = [[ +Prop Health Plugin for Helix + +This lightweight plugin adds a dynamic health and destruction system to all physics props. + +Features: + + - Mass-Based Health: A prop's maximum health is automatically calculated based on its physical weight (Mass * Config Multiplier). Heavier objects like concrete barriers are much harder to destroy than wooden crates. + + - Explosive Destruction: When health reaches zero, the prop creates an explosion effect before being removed. **Can be Disabled** + + - Real-Time Tooltip: Looking at a prop displays a live health counter in the standard Helix interaction menu that updates instantly during combat. + +Configuration: You can adjust 'propsMinHP' (default 50) and 'propsHPMultiplier' (default 5) in your server configuration to balance durability. + +*AI usage note: Code quality improvements.* +]] + +ix.util.Include("sv_plugin.lua") +ix.util.Include("cl_plugin.lua") + +ix.lang.AddTable("english", { + propHealth = "Health" +}) + +ix.lang.AddTable("french", { + propHealth = "Santé" +}) + +ix.config.Add("propsHPMultiplier", 5, "Multiplier for props HP.", nil, { + data = {min = 0, max = 10}, + category = "Prop Health" +}) + +ix.config.Add("propsMinHP", 50, "Minimal HP for props.", nil, { + data = {min = 0, max = 1000}, + category = "Prop Health" +}) + +ix.config.Add("propsExplode", true, "Toggles explosion effects on prop destruction.", nil, { + category = "Prop Health" +}) + diff --git a/prophealth/sv_plugin.lua b/prophealth/sv_plugin.lua new file mode 100644 index 0000000..0b72b49 --- /dev/null +++ b/prophealth/sv_plugin.lua @@ -0,0 +1,60 @@ +local PLUGIN = PLUGIN + +-- Helper function: Initializes HP and returns the value +-- Returns: maxHP (number) +local function InitPropHealth(entity) + local minHP = ix.config.Get("propsMinHP", 50) + local multiplier = ix.config.Get("propsHPMultiplier", 5) + local phys = entity:GetPhysicsObject() + + local hp = minHP + + if (IsValid(phys)) then + hp = math.max(minHP, phys:GetMass() * multiplier) + end + + -- Sync both Max and Current HP + entity:SetNetVar("propMaxHP", hp) + entity:SetNetVar("propHP", hp) + + return hp +end + +function PLUGIN:EntityTakeDamage(entity, dmgInfo) + -- Filter: Only physics props, ignore map props + if (entity:GetClass() ~= "prop_physics") then return end + if (entity:CreatedByMap()) then return end + + -- Fetch current state + local curHP = entity:GetNetVar("propHP") + local maxHP = entity:GetNetVar("propMaxHP") + + -- Lazy Initialization (if prop spawned before plugin was added or bug) + if (not curHP) then + maxHP = InitPropHealth(entity) + curHP = maxHP + end + + -- Calculate Damage + curHP = curHP - dmgInfo:GetDamage() + + -- State Handling + if (curHP <= 0) then + -- Destruction + if (ix.config.Get("propsExplode", false)) then + local effectData = EffectData() + effectData:SetOrigin(entity:GetPos()) + util.Effect("Explosion", effectData) + end + + entity:Remove() + else + -- Apply Health Update + entity:SetNetVar("propHP", curHP) + end +end + +-- Optimization: Pre-calculate HP on spawn to avoid overhead during combat +function PLUGIN:PlayerSpawnedProp(client, model, entity) + InitPropHealth(entity) +end \ No newline at end of file