Files
wnsrc/lua/stormfox2/framework/sh_weather_handle.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

447 lines
14 KiB
Lua

--[[
| 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/
--]]
-- Updates the weather for given players
local lastSet = 0
local CurrentWeather
local CurrentPercent = 1
local function isColor(t)
return t.r and t.g and t.b and true or false
end
local function Blender(nFraction, vFrom, vTo) -- Will it blend?
-- Nils should be false, if one of them is a boolean
if type(vFrom) == "nil" and type(vTo) == "boolean" then
vFrom = false
end
if type(vTo) == "nil" and type(vFrom) == "boolean" then
vTo = false
end
-- If the same value, then return it
if vTo == vFrom then return vTo end
-- In case of two diffrent variables.
if type(vFrom) ~= type(vTo) then
StormFox2.Warning("Mixer called with values of two different types[" .. type(vFrom) .. "," .. type(vTo) .. "]")
debug.Trace()
return vFrom
elseif type(vTo) == "string" or type(vTo) == "IMaterial" or type(vTo) == "boolean" then -- String, material or bool. Return vTo.
return vTo
elseif type(vTo) == "number" then -- Number
return Lerp(nFraction, vFrom, vTo)
elseif type(vTo) == "table" and isColor(vTo) then -- Color
local r = Lerp( nFraction, vFrom.r or 255, vTo.r )
local g = Lerp( nFraction, vFrom.g or 255, vTo.g )
local b = Lerp( nFraction, vFrom.b or 255, vTo.b )
local a = Lerp( nFraction, vFrom.a or 255, vTo.a )
return Color( r, g, b, a )
end
--StormFox2.Warning("ERROR: Unsupported mix value type[" .. type(vTo) .. "]. Returning original value")
--debug.Trace()
return vFrom
end
local function IsSame(sName, nPercentage, nDelta)
if CurrentPercent ~= nPercentage then return false end
if not CurrentWeather then return false end
return CurrentWeather.Name == sName
end
local function ApplyWeather(sName, nPercentage, nDelta)
hook.Run("StormFox2.weather.prechange", sName ,nPercentage )
if nDelta and nDelta <= 0 then
nDelta = nil
elseif nDelta then
local sp = StormFox2.Time.GetSpeed_RAW()
if sp > 0 then
nDelta = nDelta / sp
end
end
local bSameWeather = sName == (CurrentWeather and CurrentWeather.Name or "Clear")
if CurrentWeather and CurrentWeather.OnChange then
CurrentWeather:OnChange( sName, nPercentage, nDelta )
end
if CurrentWeather and CurrentWeather.OnRemove and not bSameWeather then
CurrentWeather:OnRemove( sName, nPercentage, nDelta )
end
local clear = StormFox2.Weather.Get( "Clear" )
CurrentWeather = StormFox2.Weather.Get( sName )
CurrentPercent = nPercentage
local stamp = StormFox2.Sky.GetLastStamp()
if sName == "Clear" then
nPercentage = 1
end
if nPercentage >= 1 then
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
local v = CurrentWeather:Get( key, stamp )
if type(v) == "table" and not (v.r and v.g and v.b) then
StormFox2.Data.Set(key, v)
else
StormFox2.Data.Set(key, v, nDelta)
end
end
elseif nPercentage <= 0 then
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
StormFox2.Data.Set(key, clear:Get( key, stamp ), nDelta)
end
else -- Mixing bin
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
local var2,b_nomix = CurrentWeather:Get( key, stamp )
local d = nDelta
if type(var2) == "table" and not (var2.r and var2.g and var2.b) then
d = nil
end
if b_nomix then
StormFox2.Data.Set(key, var2, d)
else
local var1 = clear:Get( key, stamp )
if var2 and not var1 then -- This is not a default variable
if type(var2) == "number" then
var1 = 0
end
end
if not var1 and var2 then -- THis is not a default varable
StormFox2.Data.Set(key, var2, d)
elseif var1 and var2 then
StormFox2.Data.Set(key, Blender(nPercentage, var1, var2), d)
end
end
end
end
if CurrentWeather.Init and not bSameWeather then
CurrentWeather.Init()
end
if CurrentWeather.Tick10 then
CurrentWeather.Tick10()
end
hook.Run("StormFox2.weather.postchange", sName ,nPercentage, nDelta )
end
hook.Add("StormFox2.Sky.StampChange","StormFox2.Weather.Stamp",function(_,nLerpTime)
ApplyWeather(CurrentWeather and CurrentWeather.Name or "Clear", CurrentPercent, nLerpTime)
end)
---Returns the current weather-type.
---@return Weather
function StormFox2.Weather.GetCurrent()
return CurrentWeather or StormFox2.Weather.Get( "Clear" )
end
---Returns the current weather percent.
---@return number Percent
function StormFox2.Weather.GetPercent()
return StormFox2.Data.Get("w_Percentage",CurrentPercent)
end
---Returns the weather percent we're lerping to.
---@return number
function StormFox2.Weather.GetFinishPercent()
return CurrentPercent
end
---Returns the current weather description. Like 'Snow', 'Storm' .. ect.
---Second argument isn't translated.
---@return string Description
---@return string Description_Untranslated
function StormFox2.Weather.GetDescription()
local c = StormFox2.Weather.GetCurrent()
if not c.GetName then
return c.Name
end
local a,b = c:GetName(StormFox2.Time.Get(), StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), StormFox2.Weather.GetPercent())
return a,b or a
end
local errM = Material("error")
---Returns the current weather-icon.
---@return userdata Material
function StormFox2.Weather.GetIcon()
local c = StormFox2.Weather.GetCurrent()
if not c.GetIcon then
return errM
end
return c.GetIcon(StormFox2.Time.Get(), StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), StormFox2.Weather.GetPercent())
end
local SF_UPDATE_WEATHER = 0
local SF_INIT_WEATHER = 1
if SERVER then
local l_data
---Sets the weather.
---@server
---@param sName string
---@param nPercentage number
---@param nDelta? number
---@return boolean success
function StormFox2.Weather.Set( sName, nPercentage, nDelta )
if not StormFox2.Setting.GetCache("enable", true) then return end -- Just in case
if nDelta and l_data and nDelta == l_data then
if IsSame(sName, nPercentage) then return false end
end
l_data = nDelta
-- Default vals
if not nDelta then
nDelta = 4
end
if not nPercentage then
nPercentage = 1
end
-- Unknown weathers gets replaced with 'Clear'
if not StormFox2.Weather.Get( sName ) then
StormFox2.Warning("Unknown weather: " .. tostring(sName))
sName = "Clear"
end
-- In case we set the weather to clear, change it so it is the current weather at 0 instead
if sName == "Clear" and CurrentWeather and nDelta > 0 then
nPercentage = 0
sName = CurrentWeather.Name
elseif sName == "Clear" then
nPercentage = 1
end
lastSet = CurTime()
net.Start( StormFox2.Net.Weather )
net.WriteBit(SF_UPDATE_WEATHER)
net.WriteUInt( math.max(0, StormFox2.Data.GetLerpEnd( "w_Percentage" )), 32)
net.WriteFloat(nPercentage)
net.WriteString(sName)
net.WriteFloat(CurTime() + nDelta)
net.Broadcast()
ApplyWeather(sName, nPercentage, nDelta)
if sName == "Clear" then
nPercentage = 0
end
StormFox2.Data.Set("w_Percentage",nPercentage,nDelta)
return true
end
net.Receive( StormFox2.Net.Weather, function(len, ply) -- OI, what weather?
local lerpEnd = StormFox2.Data.GetLerpEnd( "w_Percentage" )
net.Start( StormFox2.Net.Weather )
net.WriteBit(SF_INIT_WEATHER)
net.WriteUInt( math.max(0, StormFox2.Data.GetLerpEnd( "w_Percentage" )), 32)
net.WriteFloat( CurrentPercent )
net.WriteString( StormFox2.Weather.GetCurrent().Name )
net.WriteFloat( StormFox2.Data.Get("w_Percentage",CurrentPercent) )
net.Send(ply)
end)
-- Handles the terrain logic
timer.Create("StormFox2.terrain.updater", 4, 0, function()
local cW = StormFox2.Weather.GetCurrent()
local cT = StormFox2.Terrain.GetCurrent()
if not cW then return end -- No weather!?
local terrain = cW:Get("Terrain")
if not cT and not terrain then return end -- No terrain detected
if cT and terrain and cT == terrain then return end -- Same terrain detected
if terrain then -- Switch terraintype out. This can't be the same as the other
StormFox2.Terrain.Set(terrain.Name)
elseif not terrain and not cT.lock then -- This terrain doesn't have a lock. Reset terrain
StormFox2.Terrain.Reset()
elseif not terrain and cT.lock then -- Check the lock of cT and see if we can reset
if cT:lock() then -- Lock tells us we can reset the terrain
StormFox2.Terrain.Reset()
end
end
end)
local tS = CurTime()
-- In case no weather was set
timer.Simple(8, function()
-- Clear up weather when it reaches 0
timer.Create("StormFox2.weather.clear",1,0,function()
if not CurrentWeather then return end
if CurrentWeather.Name == "Clear" then return end
local p = StormFox2.Weather.GetPercent()
if p <= 0 then
StormFox2.Weather.Set("Clear", 1, 0)
end
end)
if CurrentWeather then return end
StormFox2.Weather.Set("Clear", 1, 0)
end)
else
local hasLocalWeather = false
local svWeather
local function SetW( sName, nPercentage, nDelta )
-- Block same weather
if IsSame(sName, nPercentage) then return false end
ApplyWeather(sName, nPercentage, nDelta)
if sName == "Clear" then
nPercentage = 0
end
StormFox2.Data.Set("w_Percentage",nPercentage,nDelta)
end
---Sets the weather on the client. Server-side stuff won't be set.
---@client
---@param sName? string
---@param nPercentage? number
---@param nDelta? number
---@param nTemperature? number
function StormFox2.Weather.SetLocal( sName, nPercentage, nDelta, nTemperature)
-- If nil then remove the local weather
if not sName then
return StormFox2.Weather.RemoveLocal()
end
-- Unknown weathers gets replaced with 'Clear'
if not StormFox2.Weather.Get( sName ) then
StormFox2.Warning("Unknown weather: " .. tostring(sName))
sName = "Clear"
end
if not hasLocalWeather then
svWeather = {StormFox2.Weather.GetCurrent().Name, StormFox2.Weather.GetFinishPercent(), StormFox2.Temperature.Get()}
end
StormFox2.Temperature.SetLocal(nTemperature)
-- Block same weather
SetW(sName, nPercentage or 1, nDelta)
hasLocalWeather = true
end
---Removes the local weather.
---@client
function StormFox2.Weather.RemoveLocal()
if not hasLocalWeather then return end
SetW(svWeather[1], svWeather[2], 4)
StormFox2.Temperature.SetLocal(nil)
svWeather = nil
hasLocalWeather = false
end
net.Receive( StormFox2.Net.Weather, function(len)
local flag = net.ReadBit() == SF_UPDATE_WEATHER
local wTarget = net.ReadUInt(32)
local nPercentage = net.ReadFloat()
local sName = net.ReadString()
if flag then
local nDelta = net.ReadFloat() - CurTime()
-- Calculate the time since server set this
if not hasLocalWeather then
SetW(sName, nPercentage, nDelta)
else
svWeather[1] = sName
svWeather[2] = nPercentage
end
else
local current = net.ReadFloat()
if not hasLocalWeather then
local secondsLeft = wTarget - CurTime()
if secondsLeft <= 0 then
SetW(sName, nPercentage, 0)
else
SetW(sName, current, 0)
SetW(sName, nPercentage, secondsLeft)
end
else
svWeather[1] = sName
svWeather[2] = nPercentage
end
end
end)
-- Ask the server what weather we have
hook.Add("StormFox2.InitPostEntity", "StormFox2.terrain", function()
net.Start( StormFox2.Net.Weather )
net.SendToServer()
end)
end
hook.Add("Think", "StormFox2.Weather.Think", function()
if not CurrentWeather then return end
if not CurrentWeather.Think then return end
if not StormFox2.Setting.SFEnabled() then return end
CurrentWeather:Think()
end)
timer.Create("StormFox2.Weather.tickslow", 1, 0, function()
if not CurrentWeather then return end
if not CurrentWeather.TickSlow then return end
CurrentWeather.TickSlow()
end)
hook.Add("StormFox2.weather.postchange", "StormFox2.weather.slowtickinit", function()
if not CurrentWeather then return end
if not CurrentWeather.TickSlow then return end
CurrentWeather.TickSlow()
end)
if CLIENT then
local c_tab = {"PostDrawTranslucentRenderables", "PreDrawTranslucentRenderables", "HUDPaint"}
for i,v in ipairs(c_tab) do
hook.Add(v, "StormFox2.Weather." .. v, function(...)
if not CurrentWeather then return end
if not CurrentWeather[v] then return end
CurrentWeather[v](...)
end)
end
end
-- Some functions to make it easier.
---Returns true if it is raining, or if current weather is child of rain.
---@return boolean
---@shared
function StormFox2.Weather.IsRaining()
local wT = StormFox2.Weather.GetCurrent()
if wT.Inherit == "Rain" then return true end
if wT.Name ~= "Rain" then return false end
return StormFox2.Temperature.Get() > -2 or false
end
---Returns true if it is snowing.
---@return boolean
---@shared
function StormFox2.Weather.IsSnowing()
local wT = StormFox2.Weather.GetCurrent()
if wT.Name ~= "Rain" then return false end
return StormFox2.Temperature.Get() <= -2 or false
end
---Returns the rain / snow amount. Between 0 - 1.
---@return number
---@shared
function StormFox2.Weather.GetRainAmount()
if not StormFox2.Weather.IsRaining() then return 0 end
return StormFox2.Weather.GetPercent()
end
---Returns true if the current weather is raining, snowing or inherit from rain.
---@return boolean
---@shared
function StormFox2.Weather.HasDownfall()
local wT = StormFox2.Weather.GetCurrent()
if wT.Inherit == "Rain" then return true end
return wT.Name == "Rain"
end
-- Downfall
---Returns true if the entity is hit by rain or any downfall.
---@param eEnt Entity
---@param bDont_cache boolean
---@return boolean
---@shared
function StormFox2.DownFall.IsEntityHit(eEnt, bDont_cache)
if not StormFox2.Weather.HasDownfall() then return false end
return (StormFox2.Wind.IsEntityInWind(eEnt,bDont_cache))
end
---Checks to see if the given point is hit by rain.
---@param vPos Vector
---@return boolean
---@shared
function StormFox2.DownFall.IsPointHit(vPos)
if not StormFox2.Weather.HasDownfall() then return false end
local t = util.TraceLine( {
start = vPos,
endpos = vPos + -StormFox2.Wind.GetNorm() * 262144,
mask = StormFox2.DownFall.Mask
} )
return t.HitSky
end