mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
499 lines
15 KiB
Lua
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 |