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

499 lines
15 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/
--]]
StormFox2.WeatherGen = StormFox2.WeatherGen or {}
-- Settings
local autoWeather = StormFox2.Setting.AddSV("auto_weather",true,nil, "Weather")
local hideForecast = StormFox2.Setting.AddSV("hide_forecast",false,nil, "Weather")
-- OpenWeatherMap
--[[
Some of these settings are fake, allow the client to set the settings, but never retrive the secured ones.
So you can't see the correct location or api_key and variables won't be networked to the clients.
]]
StormFox2.Setting.AddSV("openweathermap_key", "", nil,"Weather") -- Fake'ish setting
StormFox2.Setting.AddSV("openweathermap_location", "", nil,"Weather") -- Fake'ish setting
StormFox2.Setting.AddSV("openweathermap_city","",nil,"Weather") -- Fake'ish setting
local OWEnabled = StormFox2.Setting.AddSV("openweathermap_enabled",false,nil,"Weather")
StormFox2.Setting.AddSV("openweathermap_lat",52,nil,"Weather",-180,180) -- Unpercise setting
StormFox2.Setting.AddSV("openweathermap_lon",-2,nil,"Weather",-180,180) -- Unpercise setting
-- Generator
StormFox2.Setting.AddSV("min_temp",-10,nil,"Weather")
StormFox2.Setting.AddSV("max_temp",20,nil, "Weather")
StormFox2.Setting.AddSV("max_wind",50,nil, "Weather")
StormFox2.Setting.AddSV("addnight_temp",-7,nil, "Weather")
local function toStr( num )
local c = tostring( num )
return string.rep("0", 4 - #c) .. c
end
local default
local function SplitSetting( str )
if #str< 20 then return default end -- Invalid, use default
local tab = {}
local min = math.min(100, string.byte(str, 1,1) - 33 ) / 100
local max = math.min(100, string.byte(str, 2,2) - 33 ) / 100
tab.amount_min = math.min(min, max)
tab.amount_max = math.max(min, max)
local min = math.min(1440,tonumber( string.sub(str, 3, 6) ) or 0)
local max = math.min(1440,tonumber( string.sub(str, 7, 10) ) or 0)
tab.start_min = math.min(min, max)
tab.start_max = math.max(min, max)
local min = tonumber( string.sub(str, 11, 14) ) or 0
local max = tonumber( string.sub(str, 15, 18) ) or 0
tab.length_min = math.min(min, max)
tab.length_max = math.max(min, max)
tab.thunder = string.sub(str, 19, 19) == "1"
tab.pr_week = tonumber( string.sub(str, 20) ) or 0
return tab
end
local function CombineSetting( tab )
local c =string.char( 33 + (tab.amount_min or 0) * 100 )
c = c .. string.char( 33 + (tab.amount_max or 0) * 100 )
c = c .. toStr(math.Clamp( math.Round( tab.start_min or 0), 0, 1440 ) )
c = c .. toStr(math.Clamp( math.Round( tab.start_max or 0), 0, 1440 ) )
c = c .. toStr(math.Clamp( math.Round( tab.length_min or 360 ), 180, 9999) )
c = c .. toStr(math.Clamp( math.Round( tab.length_max or 360 ), 180, 9999) )
c = c .. (tab.thunder and "1" or "0")
c = c .. tostring( tab.pr_week or 2 )
return c
end
local default_setting = {}
default_setting["Rain"] = CombineSetting({
["amount_min"] = 0.4,
["amount_max"] = 0.9,
["start_min"] = 300,
["start_max"] = 1200,
["length_min"] = 360,
["length_max"] = 1200,
["pr_week"] = 3
})
default_setting["Cloud"] = CombineSetting({
["amount_min"] = 0.2,
["amount_max"] = 0.7,
["start_min"] = 300,
["start_max"] = 1200,
["length_min"] = 360,
["length_max"] = 1200,
["pr_week"] = 3
})
default_setting["Clear"] = CombineSetting({
["amount_min"] = 1,
["amount_max"] = 1,
["start_min"] = 0,
["start_max"] = 1440,
["length_min"] = 360,
["length_max"] = 1440,
["pr_week"] = 7
})
-- Morning fog
default_setting["Fog"] = CombineSetting({
["amount_min"] = 0.15,
["amount_max"] = 0.30,
["start_min"] = 360,
["start_max"] = 560,
["length_min"] = 160,
["length_max"] = 360,
["pr_week"] = 1
})
default = CombineSetting({
["amount_min"] = 0.4,
["amount_max"] = 0.9,
["start_min"] = 300,
["start_max"] = 1200,
["length_min"] = 300,
["length_max"] = 1200,
["pr_week"] = 0
})
StormFox2.WeatherGen.ConvertSettingToTab = SplitSetting
StormFox2.WeatherGen.ConvertTabToSetting = CombineSetting
if StormFox2.Weather and StormFox2.Weather.Loaded then
for _, sName in ipairs( StormFox2.Weather.GetAll() ) do
local str = default_setting[sName] or default
StormFox2.Setting.AddSV("wgen_" .. sName,str,nil,"Weather")
end
else
hook.Add("stormfox2.postloadweather", "StormFox2.WeatherGen.Load", function()
for _, sName in ipairs( StormFox2.Weather.GetAll() ) do
local str = default_setting[sName] or default
StormFox2.Setting.AddSV("wgen_" .. sName,str,nil,"Weather")
end
end)
end
-- API
local days = 2
local days_length = days * 1440
local hours_8 = 60 * 8
forecast = forecast or {}
local nul_icon = Material("gui/noicon.png")
-- Is it rain or inherits from rain?
local function isWTRain(wT)
if wT.Name == "Rain" then return true end
if wT.Inherit == "Rain" then return true end
return false
end
local function fkey( x, a, b )
return (x - a) / (b - a)
end
-- The tables are already in order.
local function findNext( tab, time ) -- First one is time
for i, tab in ipairs( tab ) do
if time > tab[1] then continue end
return i
end
return 0
end
local function calcPoint( tab, time )
local i = findNext( tab, time )
local _next = tab[i]
local _first = tab[i - 1]
local _procent = 1
if not _first then
_first = _next
else
_procent = fkey(time, _first[1], _next[1])
end
return _procent, _first, _next
end
net.Receive("StormFox2.weekweather", function(len)
forecast = {}
forecast.unix_time = net.ReadBool()
forecast.temperature = net.ReadTable()
forecast.weather = net.ReadTable()
forecast.wind = net.ReadTable()
forecast.windyaw = net.ReadTable()
for _, v in pairs( forecast.temperature ) do
if not forecast._minTemp then
forecast._minTemp = v[2]
else
forecast._minTemp = math.min(forecast._minTemp, v[2])
end
if not forecast._maxTemp then
forecast._maxTemp = v[2]
else
forecast._maxTemp = math.max(forecast._maxTemp, v[2])
end
end
if not forecast._minTemp then return end -- Invalid forecast
-- Make sure there is at least 10C between
local f = 10 - math.abs(forecast._minTemp - forecast._maxTemp)
if f > 0 then
forecast._minTemp = forecast._minTemp - f / 2
forecast._maxTemp = forecast._maxTemp + f / 2
end
-- Calculate / make a table for each 4 hours
forecast._ticks = {}
local lastW
for i = 0, days_length + 1440, hours_8 do
local _first = findNext( forecast.weather, i - hours_8 / 2 )
local _last = findNext( forecast.weather, i + hours_8 / 2 ) or _first
if not _first then continue end --????
local m = 0
local w_type = {
["fAmount"] = 0,
["sName"] = "Clear"
}
for i = _first, _last do
local w_data = forecast.weather[i]
if not w_data then continue end
if w_data[2].fAmount == 0 or w_data[2].fAmount < m then continue end
m = w_data[2].fAmount
w_type = w_data[2]
end
local _tempP, _tempFirst, _tempNext = calcPoint( forecast.temperature, i )
if _tempNext then
--local _tempP = fkey( i, )
forecast._ticks[i] = {
["fAmount"] = w_type.fAmount,
["sName"] = w_type.sName,
["nTemp"] = Lerp(_tempP, _tempFirst[2], _tempNext[2]),
["bThunder"] = w_type.bThunder or nil
}
end
end
--PrintTable(forecast)
hook.Run("StormFox2.WeatherGen.ForcastUpdate")
end)
---Returns the forecast data.
---@return table
---@client
function StormFox2.WeatherGen.GetForecast()
return forecast
end
---Returns true if we're using unix time for the forecast.
---@return boolean
---@client
function StormFox2.WeatherGen.IsUnixTime()
return forecast.unix_time or false
end
-- Render forcast
local bg = Color(26,41,72, 255)
local rc = Color(155,155,155,4)
local ca = Color(255,255,255,12)
local tempBG = Color(255,255,255,15)
local sorter = function(a,b) return a[1] < b[1] end
local m_box = Material("vgui/arrow")
local m_c = Material("gui/gradient_up")
local function DrawTemperature( x, y, w, h, t_list, min_temp, max_temp, bExpensive, offX, offY )
surface.SetDrawColor(rc)
surface.DrawRect(x, y, w, h)
surface.SetDrawColor(ca)
surface.DrawLine(x, y + h, x + w, y + h)
render.SetScissorRect( x + offX , y - 25 + offY, x + w + offX, y + h + offY, true )
local unix = StormFox2.WeatherGen.IsUnixTime()
local temp_p = fkey(0, max_temp, min_temp)
local tempdiff = max_temp - min_temp
local yT = h / tempdiff
local div = 10
if tempdiff < 25 then
div = 10
elseif tempdiff < 75 then
div = 20
elseif tempdiff < 150 then
div = 100
elseif tempdiff < 300 then
div = 200
elseif tempdiff < 500 then
div = 300
else
div = 1000
end
local s = math.ceil(min_temp / div) * div
local counts = (max_temp - s) / div
for temp = s, max_temp, div do
local tOff = temp - min_temp
local ly = y + h - (tOff * yT)
if temp == 0 then
surface.SetDrawColor(color_white)
surface.SetMaterial(m_box)
surface.DrawTexturedRectUV(x, ly, w, 1, 0, 0.5, w / 1 * 0.3, 0.6)
else
surface.SetDrawColor(ca)
surface.DrawLine(x, ly, x + w, ly)
end
end
local curTim = unix and os.time() or StormFox2.Time.Get()
local oldX, oldY, oldP
surface.SetTextColor(color_white)
surface.SetFont("SF_Display_H3")
for i, data in ipairs( t_list ) do
if not data then break end
local time_p
if unix then
time_p = (data[1] - curTim) / (1440 * 60 * 1.5)
else
time_p = (data[1] - curTim) / days_length
end
local temp_p = data[2]
local pointx = time_p * w + x
local pointy = y + h - (temp_p * h)
if oldX then
if oldX > x + w then break end
surface.SetDrawColor(color_white)
surface.DrawLine(pointx, pointy, oldX, oldY)
if bExpensive then
local triangle = {
{ x = oldX , y = oldY , u = 0,v = oldP},
{ x = pointx, y = pointy, u = 1,v = temp_p},
{ x = pointx, y = y + h , u = 1,v = 0},
{ x = oldX , y = y + h , u = 0,v = 0},
}
surface.SetMaterial(m_c)
surface.SetDrawColor(tempBG)
surface.DrawPoly( triangle )
end
surface.SetTextPos(pointx - 5, pointy - 14)
local temp = min_temp + temp_p * tempdiff
temp = math.Round(StormFox2.Temperature.GetDisplay(temp), 1) .. StormFox2.Temperature.GetDisplaySymbol()
surface.DrawText(temp)
end
oldX = pointx
oldY = pointy
oldP = temp_p
end
render.SetScissorRect(0,0,0,0,false)
--PrintTable(t_list)
end
local lM = Material("vgui/loading-rotate")
local lL = Material("stormfox2/logo.png")
local function DrawDisabled( str, w, h )
draw.DrawText(str, "SF_Display_H", w / 2, h / 4, color_white, TEXT_ALIGN_CENTER)
surface.SetDrawColor(color_white)
surface.SetMaterial(lL)
surface.DrawTexturedRectRotated(w / 2, h / 3 * 2, 64, 64, 0)
surface.SetMaterial(lM)
surface.DrawTexturedRectRotated(w / 2, h / 3 * 2, 128, 128, (CurTime() * 100)% 360)
end
---Renders the forecast.
---@param w number
---@param h number
---@param bExpensive boolean
---@param offX? number
---@param offY? number
function StormFox2.WeatherGen.DrawForecast(w,h,bExpensive, offX, offY)
offX = offX or 0
offY = offY or 0
local y = 0
surface.SetDrawColor(bg)
surface.DrawRect(0,0,w,h)
-- Check if enabled, else render disable message
if not autoWeather:GetValue() then
local s = language.GetPhrase("sf_auto_weather") or "sf_auto_weather"
local d = language.GetPhrase("#addons.preset_disabled") or "Disabled"
s = s.. ": " .. string.match(d, "%w+")
DrawDisabled( s, w, h )
return
end
if hideForecast:GetValue() then
local s = language.GetPhrase("sf_hide_forecast") or "sf_hide_forecast"
local d = language.GetPhrase("#addons.preset_enabled") or "Enabled"
s = s.. ": " .. string.match(d, "%w+")
DrawDisabled( s, w, h )
return
end
if not forecast or not forecast._minTemp then
local c = string.rep(".", CurTime() % 3 + 1)
DrawDisabled( "No data yet" .. c, w, h )
return
end
local unix = StormFox2.WeatherGen.IsUnixTime()
local curTim = unix and os.time() or StormFox2.Time.Get()
-- Draw Temperature
-- Convert it into a list of temperature w procent
local c_temp = StormFox2.Temperature.Get()
local min_temp = math.min(c_temp, forecast._minTemp)
local max_temp = math.max(c_temp, forecast._maxTemp)
local abs = math.abs(max_temp - min_temp) * 0.1
min_temp = min_temp - abs
max_temp = max_temp + abs
local t = {}
t[1] = { curTim, fkey( c_temp, min_temp, max_temp ) }
for i, data in ipairs( forecast.temperature ) do
local time = data[1]
if time <= curTim then continue end -- Ignore anything before
table.insert(t, {time, fkey( data[2], min_temp, max_temp ) } )
end
DrawTemperature( w * 0.05, h * 0.5 ,w * 0.9, h * 0.4,t, min_temp, max_temp, bExpensive, offX, offY)
-- Draw current weahter
surface.SetDrawColor(color_white)
surface.SetMaterial(StormFox2.Weather.GetIcon())
surface.SetFont("SF_Display_H")
local tex = StormFox2.Weather.GetDescription()
local tw, th = surface.GetTextSize(tex)
local wide = tw + 48
surface.DrawTexturedRect(w / 2 - 48,h * 0.05, 40,40)
draw.DrawText(tex, "SF_Display_H", w / 2 , h * 0.07, color_white, TEXT_ALIGN_LEFT)
-- Draw DayIcons
surface.SetDrawColor(color_white)
local s = w / 12
if not unix then
local c = math.ceil(curTim / hours_8) * hours_8
for i = c, days_length + c - 420, hours_8 do
-- Render Time
local t_stamp = StormFox2.Time.GetDisplay( i % 1440 )
local delt = i - curTim
local x = math.ceil(w * 0.9 / days_length * delt)
draw.DrawText(t_stamp, "SF_Display_H3", x , h * 0.9, color_white)
-- Render icon
local day = forecast._ticks[i]
if day then
local w_type = StormFox2.Weather.Get(day.sName)
if not w_type then
surface.SetMaterial(nul_icon)
else
surface.SetMaterial(w_type.GetIcon( i % 1440, day.nTemp, day.nWind or 0, day.bThunder or false, day.fAmount or 0) )
surface.DrawTexturedRect(x, h * 0.25, s, s)
local name = w_type:GetName(i % 1440, day.nTemp, day.nWind or 0, day.bThunder or false, day.fAmount or 0)
draw.DrawText(name, "SF_Display_H2", x + s / 2, h * 0.25 + s, color_white, TEXT_ALIGN_CENTER)
end
end
end
else
--(1440 * 60 * 1.5)
local wP = ( w * 0.9 ) / (1440 * 60 * 1.5)
local _12 = StormFox2.Setting.Get("12h_display", false)
local z = 0
for i, data in pairs( forecast.weather ) do
local unixT = data[1] or 0
if unixT < curTim then continue end
z = z + 1
if z > 6 then continue end
local delta = unixT - curTim
local t_stamp
local fakeTime = os.date( "%H:%M", unixT )
if _12 then
t_stamp = os.date( "%I:%M %p", unixT )
else
t_stamp = fakeTime
end
local x = math.ceil(wP * delta)
draw.DrawText("[" .. t_stamp .. "]", "SF_Display_H3", x , h * 0.9, color_white)
local day = data[2]
if day then
local n = string.Explode(":", fakeTime)
local f = n[1] * 60 + n[2]
local w_type = StormFox2.Weather.Get(day.sName)
local l_temp = 0
for id, tD in ipairs( forecast.temperature ) do
if tD[1] > unixT then break end
l_temp = tD[2]
end
local l_wind = 0
for id, tD in ipairs( forecast.wind ) do
if tD[1] > unixT then break end
l_wind = tD[2]
end
if not w_type then
surface.SetMaterial(nul_icon)
surface.DrawTexturedRect(x, h * 0.25, s, s)
else
surface.SetMaterial(w_type.GetIcon( f, l_temp, l_wind, day.bThunder or false, day.fAmount or 0) )
surface.DrawTexturedRect(x, h * 0.25, s, s)
local name = w_type:GetName(i % 1440, l_temp, l_wind, day.bThunder or false, day.fAmount or 0)
draw.DrawText(name, "SF_Display_H2", x + s / 2, h * 0.25 + s, color_white, TEXT_ALIGN_CENTER)
end
end
end
end
end