Files
wnsrc/lua/entities/stormfox_streetlight_invisible.lua

391 lines
13 KiB
Lua
Raw Normal View History

2024-08-04 23:54:45 +03:00
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
AddCSLuaFile()
ENT.Type = "anim"
ENT.Base = "base_anim"
ENT.PrintName = "Invisible streetlight"
ENT.Author = "Nak"
ENT.Purpose = "Light area up"
ENT.Instructions = "Place it somewhere"
ENT.Category = "StormFox2"
ENT.Editable = true
ENT.Spawnable = false
ENT.AdminOnly = true
ENT.RenderGroup = RENDERGROUP_TRANSLUCENT
ENT.DisableDuplicator = true
local RENDER_DISTANCE = 3500 ^ 2
hook.Add("EntityKeyValue", "StormFox2.SLightinvis", function(ent, key, val)
if not IsValid( ent ) then return end
if ent:GetClass() ~= "stormfox_streetlight_invisible" then return end
key = key:lower()
if ent._launch then
if key == "lighttype" then
ent:SetLightType( tonumber( val ) or 0 )
elseif key == "lightcolour" then
ent:SetLightColor( Vector( val ) or Vector(1,1,1) )
elseif key == "lightcolor" then
ent:SetLightColor( Vector( val ) or Vector(1,1,1) )
elseif key == "lightbrightness" then
ent:SetLightBrightness( tonumber( val ) or 0 )
end
else
ent._launchoption = ent._launchoption or {}
ent._launchoption[key] = val
end
end)
function ENT:Initialize()
if SERVER then
self._launch = true
self:SetModel( "models/hunter/blocks/cube025x025x025.mdl" )
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_NONE )
self:SetSolid( SOLID_VPHYSICS )
self:AddFlags( FL_WORLDBRUSH )
self:AddEFlags(EFL_NO_PHYSCANNON_INTERACTION)
self:SetKeyValue("gmod_allowphysgun", 0)
self:AddEFlags( EFL_NO_DAMAGE_FORCES )
self:SetCollisionGroup(COLLISION_GROUP_WORLD)
self:DrawShadow(false)
self:SetLightColor(Vector(1,1,1))
self:SetLightBrightness(1)
if self._launchoption then
if self._launchoption["lighttype"] then
self:SetLightType( tonumber( self._launchoption["lighttype"] ) )
end
local col = self._launchoption["lightcolor"] or self._launchoption["lightcolour"]
if col then
self:SetLightColor( Vector( col ) or Vector(1,1,1) )
end
if self._launchoption["lightbrightness"] then
self:SetLightBrightness( tonumber( self._launchoption["lightbrightness"] ) )
end
end
end
end
hook.Add( "PhysgunPickup", "StormFox2.StreetLight.DisallowPickup", function( ply, ent )
if ent and ent:GetClass() == "stormfox_streetlight_invisible" then return false end
end )
hook.Add("CanPlayerUnfreeze", "StormFox2.StreetLight.DisallowUnfreeze", function( ply, ent )
if ent and ent:GetClass() == "stormfox_streetlight_invisible" then return false end
end)
local INVALID = 0
local POINTLIGHT= 1
local SPOTLIGHT = 2
local FAKESPOT = 3
local options = {
["2D Sprite"] = POINTLIGHT,
["2D Beam"] = FAKESPOT,
["ProjectedTexture"] = SPOTLIGHT
}
function ENT:SetupDataTables()
self:NetworkVar( "Int", 0, "LightType", { KeyName = "LightType", Edit = { type = "Combo", values = options } })
self:NetworkVar( "Vector", 0, "LightColor",{ KeyName = "LightColor", Edit = { type = "VectorColor" } } )
self:NetworkVar( "Float", 0, "LightBrightness",{ KeyName = "Brightness", Edit = { type = "Float", min = 0, max = 10 } } )
end
function ENT:CanProperty(_, str)
if str == "skin" then return false
elseif str == "drive" then return false
elseif str == "collision" then return false
elseif str == "persist" then return true
end
return true
end
if CLIENT then
local base_mat = Material("stormfox2/entities/streetlight_invis")
local base_mat2 = Material("stormfox2/entities/streetlight_invis_point")
local base_mat3 = Material("stormfox2/entities/streetlight_invis_beam")
local equip_tool = false
local d_tab = {}
hook.Add("PostRender", "StormFox2.StreetLights.Update", function()
d_tab = {}
equip_tool = false
local wep = LocalPlayer():GetActiveWeapon()
if not wep or not IsValid(wep) then return end
if wep:GetClass() ~= "sf2_tool" then return end
equip_tool = true
d_tab = ents.FindByClass("stormfox_streetlight_invisible")
end)
hook.Add("PreDrawHalos", "StormFox2.StreetLights.Halo", function()
if not equip_tool then return end
if not d_tab or #d_tab < 1 then return end
halo.Add( d_tab, color_white, 2, 2, 1, true,true )
end)
function ENT:DrawSelfCheck()
if not equip_tool then return end
render.SetMaterial(base_mat)
render.DrawBox(self:GetPos(), self:GetAngles(), self:OBBMins(), self:OBBMaxs(), color_white)
if self:GetLightType() == POINTLIGHT then
render.SetMaterial(base_mat2)
else
render.SetMaterial(base_mat3)
end
local s = self:OBBMaxs().x
local f = self:GetAngles():Up()
render.DrawQuadEasy(self:GetPos() + -f * s * 1.005, -f, s * 2, s * 2, color_white, 0)
end
-- We use a variable to tell if we should render any lights nearby. Since it can be is costly.
local m_render = false
local sorter = function(a,b)
return a[2] < b[2]
end
local Max_PointLight = 10
local Max_SpotLight = 4
local Max_ShadowLight = 2
local Max_Fake = 40
_STORMFOX2_PTLIGHT = _STORMFOX2_PTLIGHT or {
ents = {},
used = 0
}
-- Handles and returns a project texture.
-- Returns the projected texture and ID
local function RequestLight()
_STORMFOX2_PTLIGHT.used = _STORMFOX2_PTLIGHT.used + 1
if _STORMFOX2_PTLIGHT.used > Max_SpotLight then return end -- Max 4
local pfent = _STORMFOX2_PTLIGHT.ents[_STORMFOX2_PTLIGHT.used]
if IsValid(pfent) then
pfent:SetEnableShadows( _STORMFOX2_PTLIGHT.used < Max_ShadowLight )
return pfent, _STORMFOX2_PTLIGHT.used
end
_STORMFOX2_PTLIGHT.ents[_STORMFOX2_PTLIGHT.used] = ProjectedTexture()
pfent = _STORMFOX2_PTLIGHT.ents[_STORMFOX2_PTLIGHT.used]
if not IsValid(fent) then return end
pfent:SetTexture( "effects/flashlight001" )
pfent:SetBrightness(2)
pfent:SetEnableShadows( _STORMFOX2_PTLIGHT.used < Max_ShadowLight )
return pfent, _STORMFOX2_PTLIGHT.used
end
local cost = {}
local row = 0
hook.Add("PostDrawTranslucentRenderables", "StormFox2.Streetlights", function(a, b, c)
if a or b or c or not (m_render or _STORMFOX2_PTLIGHT.ents[1]) then return end
-- Check the light-range
local b_on = StormFox2.Map.GetLightRaw() < 20
if row < 6 and b_on then -- On
row = math.min(row + FrameTime() * 0.75, 6)
elseif row > 0 and not b_on then
row = math.max(row - FrameTime() * 0.95, 0)
end
if row <= 0 and not _STORMFOX2_PTLIGHT.ents[1] then return end -- No lights are on. No need to calculate things
local view = StormFox2.util.GetCalcView()
local rp = view.pos + view.ang:Forward() * 250
-- Sort the lights. So we get the closest first
local t = {}
local nID = math.Round(row, 0)
for k, v in ipairs(ents.FindByClass("stormfox_streetlight_invisible")) do
if v:EntIndex() % 6 >= row then continue end
table.insert(t, {v,math.max(0, v:GetPos():DistToSqr(rp) - (v._tDis2 or 0))})
end
table.sort(t, sorter)
-- Setup cost calculation
cost[1] = Max_PointLight
cost[2] = Max_SpotLight
cost[3] = Max_Fake
local vAng = view.ang
local vNorm = vAng:Forward()
_STORMFOX2_PTLIGHT.used = 0
-- Draw lights
local fD = math.Clamp((1 - (StormFox2.Fog.GetDistance() / 6000)),0, 1)
for _, tab in ipairs(t) do
local n = 1 - (tab[2] / RENDER_DISTANCE)
if n <= 0 then continue end
tab[1]:DrawLight(n * (0.85 + 0.45 * fD),view.pos,vAng,vNorm)
end
for i = 4, 1, -1 do
if not IsValid(_STORMFOX2_PTLIGHT.ents[i]) then continue end
if _STORMFOX2_PTLIGHT.used < i then
_STORMFOX2_PTLIGHT.ents[i]:Remove()
_STORMFOX2_PTLIGHT.ents[i] = nil
else
break
end
end
m_render = false
end)
function ENT:DrawTranslucent()
if not m_render then -- Wait until some lights is in render
local rp = StormFox2.util.RenderPos()
local d = rp:DistToSqr(self:GetPos())
if d > RENDER_DISTANCE then return end
end
self:DrawSelfCheck() -- Draw if tool is out
m_render = true
end
local m_lamp = Material("stormfox2/effects/light_beam")
local col = Color(255,255,255,155)
function ENT:TraceDown()
if self._tPos then return self._tPos, self._tDis, self._norm end
local norm = -self:GetAngles():Up()
local tr = util.TraceLine({
start = self:GetPos() + norm * 100,
endpos = self:GetPos() + norm * 1000,
filter = self,
mask = MASK_SOLID_BRUSHONLY
})
self._tPos = tr.HitPos or self:GetPos()
self._tDis = self._tPos:Distance(self:GetPos())
self._tDis2 = self._tDis^2
self._norm = norm
return self._tPos, self._tDis, norm
end
local m_spot = Material('stormfox2/effects/spotlight')
local m_flash = Material('stormfox2/effects/flashlight_a')
function ENT:DrawPoint(dist, add, size, ignoreB)
size = (size or 80) * (not ignoreB and self:GetLightBrightness() or 1)
local v = self:GetLightColor()
col.a = 255 * dist
col.r = v.x * 255
col.g = v.y * 255
col.b = v.z * 255
render.SetMaterial(m_spot)
if add then
render.DrawSprite(self:GetPos() + add, size, size, col)
else
render.DrawSprite(self:GetPos(), size, size, col)
end
end
local c_flash = Color(255,255,255,255)
function ENT:DrawSpot(dist, fake)
dist = (dist - .5) * 2
if dist <= 0 then return end
if not fake then
local pEnt, id = RequestLight()
if not IsValid(pEnt) then return end
local v = self:GetLightColor()
local bright = math.Round(dist * 2, 1) * self:GetLightBrightness()
if self._lastB == bright and (self._lastID or -1) == id and self._r == v.x and self._g == v.y and self._b == v.z then return end
self._lastB = bright
self._r = v.x
self._g = v.y
self._b = v.z
pEnt:SetBrightness(bright)
local pos, dis, norm = self:TraceDown()
local up = self:GetAngles():Up()
local aDown = -up:Angle()
pEnt:SetPos(self:GetPos() + norm * 20)
pEnt:SetAngles(norm:Angle())
pEnt:SetFarZ( dis * 1.2 )
pEnt:SetTexture( "effects/flashlight001" )
pEnt:SetColor(Color(v.x * 255, v.y * 255, v.z * 255))
pEnt:Update()
self._lastID = id
else
local pos, dis = self:TraceDown()
local up = self:GetAngles():Up()
c_flash.a = math.min(255, 15 * dist * self:GetLightBrightness())
local v = self:GetLightColor()
if c_flash.a <= 0 then return end
local s = (dis - 20) * 1.6
render.SetMaterial(m_flash)
render.DrawQuadEasy(pos + up, up, s, s, c_flash, 0)
end
end
-- Dist goes from 1 to 0. Where 0 is RENDER_DISTANCE units away.
function ENT:DrawBeam(dist, vPos)
local vNorm2 = (vPos - self:GetPos()):Angle():Forward()
local pos, dis = self:TraceDown()
local up = self:GetAngles():Up()
local dot = up:Dot(vNorm2)
local abs_dot = math.abs(dot)
if abs_dot < 0.9 then
local a = dist * 255 * math.Clamp(0.9 - abs_dot,0,1)
col.a = math.Clamp(a * self:GetLightBrightness(), 0, 255)
local v = self:GetLightColor()
col.r = v.x * 255
col.g = v.y * 255
col.b = v.z * 255
render.SetMaterial(m_lamp)
--local m = (pos - self:GetPos()):GetNormalized()
--render.StartBeam( 3 )
-- render.AddBeam( self:GetPos(), dis * 0.8, 0, col )
-- render.AddBeam( self:GetPos() + m * dis / 8,dis * 0.8, 0.1, col )
-- render.AddBeam( pos , dis * 0.8, 0.99, col )
--render.EndBeam()
render.DrawBeam(self:GetPos(),pos , dis * 0.8, 0, 0.99, col)
end
if dot < 0 then
self:DrawPoint(dist * -dot, vNorm2 * 15 - up * 2, dis / 3, true)
end
end
function ENT:DrawLight(dist,vPos,vAng,vNorm)
dist = math.min(dist, 1)
local lType = self:GetLightType()
if lType == 0 then return end -- Invalid
local fake = false
if cost[lType] <= 0 then-- Ignore
if lType == SPOTLIGHT then
fake = true
end
end
cost[lType] = cost[lType] - 1
if lType == POINTLIGHT then
self:DrawPoint(dist)
elseif lType == FAKESPOT then
self:DrawBeam(dist,vPos,vNorm)
elseif lType == SPOTLIGHT then
self:DrawBeam(dist,vPos,vNorm)
self:DrawSpot(dist, fake)
end
end
else -- Save
local file_location = "stormfox2/streetlights/" .. game.GetMap() .. ".json"
hook.Add( "ShutDown", "StormFox2.Streetlights.Save", function()
local tab = {}
for k, ent in ipairs(ents.FindByClass("stormfox_streetlight_invisible")) do
if ent:CreatedByMap() then continue end
table.insert(tab, {
ent:GetLightType(),
ent:GetPos(),
ent:GetAngles(),
ent:GetLightColor(),
ent:GetLightBrightness()
})
end
local out = util.TableToJSON( tab )
StormFox2.FileWrite(file_location, out)
end)
hook.Add( "InitPostEntity", "StormFox2.Streetlights.Load", function()
local fil = file.Read(file_location, "DATA" )
if not fil then return end
local tab = util.JSONToTable( fil )
if ( !tab ) then return end
for k,v in ipairs(tab) do
local ent = ents.Create("stormfox_streetlight_invisible")
ent:SetLightType(v[1])
ent:SetPos(v[2])
ent:SetAngles(v[3])
ent:Spawn()
if not v[4] then v[4] = Vector(1,1,1) end
ent:SetLightColor(Vector(v[4].x, v[4].y, v[4].z))
ent:SetLightBrightness(v[5] or 1)
end
end )
end