All pastes #735280 Raw Edit

Untitled

public text v1 · immutable
·published 2007-10-13 13:57 UTC
rendered paste body
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