TOOL.Category = "Apply"
TOOL.Name = "#Breakable Props Tool"
TOOL.Command = nil
TOOL.ConfigName = ""
-- Tables
local Breakable = {}
-- ConVars
TOOL.ClientConVar["Health"] = "100"
TOOL.ClientConVar["Multiply"] = "3"
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() * self:GetClientNumber("Multiply")
local Attached = self:GetClientNumber("Attached")
HP = math.floor(HP)
if self:GetClientNumber("Use") == 1 then
HP = math.floor(self:GetClientNumber("Health"))
end
local r,g,b,a = Trace.Entity:GetColor()
Trace.Entity.Breakable = {}
Trace.Entity.Breakable.Cur = HP
Trace.Entity.Breakable.Max = HP
Trace.Entity.Breakable.FColR = r
Trace.Entity.Breakable.FColG = g
Trace.Entity.Breakable.FColB = b
Trace.Entity.Breakable.FColA = a
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
local r,g,b,a = V.Entity:GetColor()
V.Breakable = {}
V.Breakable.Cur = HP
V.Breakable.Max = HP
V.Breakable.FColR = r
V.Breakable.FColG = g
V.Breakable.FColB = b
V.Breakable.FColA = a
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.Breakable and V.Breakable.Max != 0) then
Breakable.Extinguish(V)
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
if Entity.Breakable.FColR != nil and Entity.Breakable.FColG != nil and Entity.Breakable.FColB != nil and Entity.Breakable.FColA != nil then
Entity:SetColor(
Entity.Breakable.FColR,
Entity.Breakable.FColG,
Entity.Breakable.FColB,
Entity.Breakable.FColA
)
end
Entity.Breakable.Cur = HP
timer.Remove("Destroy:"..Entity:EntIndex())
if (Entity:IsOnFire()) then
Entity:Extinguish()
end
Breakable.DeleteEntityTrail(Entity)
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 and 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)
local smoke = Entity.Breakable.Smoke
if smoke and smoke:IsValid() then
local COLHP = 2.55*(Entity.Breakable.Cur / Entity.Breakable.Max *100) -- 255 to 0 based on HP
smoke:Fire("addoutput","startcolor".." "..tostring(COLHP).." "..tostring(COLHP).." "..tostring(COLHP))
smoke:Fire("addoutput","endcolor".." "..tostring(COLHP).." "..tostring(COLHP).." "..tostring(COLHP))
end
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) then
Breakable.CreateEntitySmokeTrail(Entity)
end
local r,g,b,a = Entity:GetColor()
if Entity.Breakable.FColR != nil and Entity.Breakable.FColG != nil and Entity.Breakable.FColB != nil and Entity.Breakable.FColA != nil then
Entity:SetColor(
Entity.Breakable.FColR * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColG * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColB * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColA
)
end
if (Entity.Breakable.Cur <= 0) then
local function Func(Entity)
if (Entity:IsValid()) then
local OldVel = Entity:GetPhysicsObject():GetVelocity()
local Wreckage = ents.Create( "wreckedstuff" )
Wreckage:SetModel( Entity:GetModel() )
Wreckage:SetAngles( Entity:GetAngles() )
Wreckage:SetPos( Entity:GetPos() )
Wreckage:Spawn()
Wreckage:Activate()
Wreckage:GetPhysicsObject():SetVelocity(OldVel)
Entity:Remove()
end
end
if Entity.Breakable.FColR != nil and Entity.Breakable.FColG != nil and Entity.Breakable.FColB != nil and Entity.Breakable.FColA != nil then
Entity:SetColor(
Entity.Breakable.FColR * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColG * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColB * (Entity.Breakable.Cur / Entity.Breakable.Max),
Entity.Breakable.FColA
)
end
timer.Create("Destroy:"..Entity:EntIndex(), 0, 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")
local INVHP = 100-(Entity.Breakable.Cur / Entity.Breakable.Max *100)
local RAD = Entity:BoundingRadius()
local COLHP = 2.55*(Entity.Breakable.Cur / Entity.Breakable.Max *100) -- 255 to 0 based on HP
local INVCOLHP = 255-(2.55*(Entity.Breakable.Cur / Entity.Breakable.Max *100))
Smoke:SetKeyValue("opacity", 1)
Smoke:SetKeyValue("spawnrate", (INVHP * 2) )
Smoke:SetKeyValue("lifetime", 4)
Smoke:SetKeyValue("startcolor", tostring(COLHP).." "..tostring(COLHP).." "..tostring(COLHP))
Smoke:SetKeyValue("endcolor", tostring(COLHP).." "..tostring(COLHP).." "..tostring(COLHP))
Smoke:SetKeyValue("minspeed", 15)
Smoke:SetKeyValue("maxspeed", 30)
Smoke:SetKeyValue("startsize", ( RAD / 2))
Smoke:SetKeyValue("endsize", ( RAD * 5 ))
Smoke:SetKeyValue("spawnradius", ( RAD / 2))
Smoke:SetKeyValue("emittime", 300)
Smoke:SetKeyValue("firesprite", "particles/smokey")
Smoke:SetKeyValue("smokesprite", "particles/smokey")
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 = 100, Command = "breakableprops_Health"})
Panel:AddControl("Slider", {Label = "#Multiply", Type = "Integer", Min = 0, Max = 100, Command = "breakableprops_Multiply"})
end