TOOL.Category = "Conna's Tools" TOOL.Name = "#Breakable Props Tool" TOOL.Command = nil TOOL.ConfigName = "" // Tables local Breakable = {} // ConVars TOOL.ClientConVar["Health"] = "100" TOOL.ClientConVar["Attached"] = "1" TOOL.ClientConVar["Use"] = "1" // Client if (CLIENT) then language.Add("Tool_breakableprops_name", "Breakable Props Tool") language.Add("Tool_breakableprops_desc", "Make an entity breakable") language.Add("Tool_breakableprops_0", "Click on an entity to make it breakable and Right click to repair it!") language.Add("Undone_breakableprops", "Undone Breakable Prop") end // Message function TOOL:Message(Text) if SERVER then self:GetOwner():SendLua("GAMEMODE:AddNotify('"..Text.."', NOTIFY_GENERIC, 10)") self:GetOwner():SendLua("surface.PlaySound('ambient/water/drip"..math.random(1, 4)..".wav')") end end // Get entities local function ExtractEntities(Entity, Entities, Constraints) Constraints = Constraints or {} Entities = Entities or {} if (!Entity:IsValid()) then return Entities, Constraints end local Index = Entity:EntIndex() Entities[Index] = Entity if (!constraint.HasConstraints(Entity)) then return Entities, Constraints end local Cons = constraint.GetTable(Entity) for K, V in pairs(Cons) do local Index = V.Constraint if (!Constraints[Index]) then Constraints[Index] = V.Constraint for B, J in pairs(V.Entity) do local E, C = ExtractEntities(J.Entity, Entities, Constraints) table.Merge(Entities, E) table.Merge(Constraints, C) end end end return Entities, Constraints end // Left click function TOOL:LeftClick(Trace) if (Trace.Entity) then if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then return false end end if(CLIENT) then return true end if(!SERVER) then return false end local HP = Trace.Entity:BoundingRadius() * 3 local Attached = self:GetClientNumber("Attached") HP = math.floor(HP) if self:GetClientNumber("Use") == 1 then HP = math.floor(self:GetClientNumber("Health")) end Trace.Entity.Breakable = {} Trace.Entity.Breakable.Cur = HP Trace.Entity.Breakable.Max = HP if Trace.Entity:GetTable().OnTakeDamage then local Prev = Trace.Entity:GetTable().OnTakeDamage function Trace.Entity:OnTakeDamage(Info) Breakable.DamagePhysicsEntity(self, Info:GetDamage()) Prev(self, Info) end end if (Wire_CreateOutputs) then Trace.Entity.Outputs = Wire_CreateOutputs(Trace.Entity, {"Health", "Max"}) end if (Wire_TriggerOutput) then Wire_TriggerOutput(Trace.Entity, "Health", HP) Wire_TriggerOutput(Trace.Entity, "Max", HP) end local Undos = {} table.insert(Undos, Trace.Entity) if (Attached == 1) then local Entities, Constraints = ExtractEntities(Trace.Entity) for K, V in pairs(Entities) do if V:IsValid() then if (V != Trace.Entity) then local HP = V:BoundingRadius() * 3 if self:GetClientNumber("Use") == 1 then HP = math.floor(self:GetClientNumber("Health")) end V.Breakable = {} V.Breakable.Cur = HP V.Breakable.Max = HP if (Wire_CreateOutputs) then V.Breakable.Outputs = Wire_CreateOutputs(V, {"Health", "Max"}) end if (Wire_TriggerOutput) then Wire_TriggerOutput(V, "Health", HP) Wire_TriggerOutput(V, "Max", HP) end if (V:GetTable().OnTakeDamage) then local Prev = V:GetTable().OnTakeDamage function V:OnTakeDamage(Info) Breakable.DamagePhysicsEntity(self, Info:GetDamage()) Prev(self, Info) end end table.insert(Undos, V) end end end end if self:GetClientNumber("Use") != 1 then self:Message("Entities health will be 3 times the objects bounding radius!") else local HP = math.floor(self:GetClientNumber("Health")) self:Message("Entities health are set to "..HP) end // Undo local function Function(Undo, Table) for K, V in pairs(Table) do if (V:IsValid()) then Breakable.Extinguish(V) V.Breakable = nil end end end undo.Create("breakableprops") undo.SetPlayer(self:GetOwner()) undo.AddFunction(Function, Undos) undo.Finish() return true end // Right cick function TOOL:RightClick(Trace) if (Trace.Entity) then if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then return false end end if(CLIENT) then if (Trace.Entity) then local Effect = EffectData() Effect:SetEntity(Trace.Entity) Effect:SetOrigin(Trace.Entity:GetPos()) Effect:SetStart(Trace.Entity:GetPos()) Effect:SetScale(999) Effect:SetMagnitude(250) util.Effect("WaterSplash", Effect) end return true end if(!SERVER) then return false end local Entities, Constraints = ExtractEntities(Trace.Entity) for K, V in pairs(Entities) do if V:IsValid() then if (V.Breakable.Max != 0) then Breakable.Extinguish(V) end end end self:Message("This prop and connected props have been restored to full health!") return true end if SERVER then // Extinguish function Breakable.Extinguish(Entity) local HP = Entity.Breakable.Max Entity.Breakable.Cur = HP timer.Remove("Destory:"..Entity:EntIndex()) if (Entity:IsOnFire()) then Entity:Extinguish() end Breakable.DeleteEntityTrail(Entity) Entity:SetColor(255, 255, 255, 255) if (Wire_TriggerOutput) then Wire_TriggerOutput(Entity, "Health", HP) Wire_TriggerOutput(Entity, "Max", HP) end end // Delete entity trail function Breakable.DeleteEntityTrail(Entity) if Entity.Breakable.Smoke:IsValid() then Entity.Breakable.Smoke:Remove() Entity.Breakable.Smoke = nil end end // Set health based on color function Breakable.ColorHealthBased(Entity, HP, Col) if (Entity.Breakable.Cur <= (Entity.Breakable.Max / HP)) then Entity:SetColor(Col, Col, Col, 255) end end // Damage a physics entity function Breakable.DamagePhysicsEntity(Entity, Amount) Amount = math.floor(Amount / 2) if (Entity.Breakable and Entity.Breakable.Cur > 0) then local HP = Entity.Breakable.Cur - Amount Entity.Breakable.Cur = HP if (Entity.Breakable.Cur <= (Entity.Breakable.Max / 2)) then Breakable.CreateEntitySmokeTrail(Entity) end Breakable.ColorHealthBased(Entity, 2, 200) Breakable.ColorHealthBased(Entity, 3, 175) Breakable.ColorHealthBased(Entity, 4, 150) Breakable.ColorHealthBased(Entity, 5, 125) Breakable.ColorHealthBased(Entity, 6, 100) Breakable.ColorHealthBased(Entity, 7, 75) if (Entity.Breakable.Cur <= 0) then local function Func(Entity) if (Entity:IsValid()) then local Effect = EffectData() Effect:SetOrigin(Entity:GetPos()) Effect:SetScale(1) Effect:SetMagnitude(25) util.Effect("Explosion", Effect, true, true) Entity:Remove() end end Entity:SetColor(50, 50, 50, 255) if not (Entity:IsOnFire()) then Entity:Ignite(30, 0) end timer.Create("Destroy:"..Entity:EntIndex(), 5, 1, Func, Entity) end if (Wire_TriggerOutput) then Wire_TriggerOutput(Entity, "Health", Entity.Breakable.Cur) Wire_TriggerOutput(Entity, "Max", Entity.Breakable.Max) end end end // Create smoke trail function Breakable.CreateEntitySmokeTrail(Entity) if not (Entity.Breakable.Smoke) then local Smoke = ents.Create("env_smoketrail") Smoke:SetKeyValue("opacity", 1) Smoke:SetKeyValue("spawnrate", 25) Smoke:SetKeyValue("lifetime", 2) Smoke:SetKeyValue("startcolor", "100 100 100") Smoke:SetKeyValue("endcolor", "125 125 125") Smoke:SetKeyValue("minspeed", 15) Smoke:SetKeyValue("maxspeed", 30) Smoke:SetKeyValue("startsize", (Entity:BoundingRadius() / 2)) Smoke:SetKeyValue("endsize", Entity:BoundingRadius()) Smoke:SetKeyValue("spawnradius", 10) Smoke:SetKeyValue("emittime", 300) Smoke:SetKeyValue("firesprite", "sprites/firetrail.spr") Smoke:SetKeyValue("smokesprite", "sprites/whitepuff.spr") Smoke:SetPos(Entity:GetPos()) Smoke:SetParent(Entity) Smoke:Spawn() Smoke:Activate() // Delete on remove Entity:DeleteOnRemove(Smoke) // Networked variables Entity.Breakable.Smoke = Smoke end end // Entity take damage function TOOL.EntityTakeDamage(Entity, One, Two, Damage) Breakable.DamagePhysicsEntity(Entity, Damage) end hook.Add("EntityTakeDamage", "TOOL.EntityTakeDamage", TOOL.EntityTakeDamage) end // Build CPanel function TOOL.BuildCPanel(Panel) Panel:AddControl("Header", {Text = "#Tool_breakableprops_name", Description = "#Tool_breakableprops_desc"}) Panel:AddControl("Checkbox", {Label = "#Make attached props breakable", Command = "breakableprops_Attached"}) Panel:AddControl("Checkbox", {Label = "#Use specified health", Command = "breakableprops_Use"}) Panel:AddControl("Slider", {Label = "#Health", Type = "Integer", Min = 0, Max = 10000, Command = "breakableprops_Health"}) end