This commit is contained in:
lifestorm
2024-08-04 23:54:45 +03:00
parent 0e770b2b49
commit df294d03aa
7526 changed files with 4011945 additions and 15 deletions

View File

@@ -0,0 +1,276 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
Render clouds
---------------------------------------------------------------------------]]
local cos,sin,rad = math.cos,math.sin,math.rad
local max,min,clamp,ceil,abs = math.max,math.min,math.Clamp,math.ceil,math.abs
local z_level = -.8
local eye_mult = -.0001
-- Generate dome mesh
local Render_Dome = Mesh()
local top_height = 20
local sc = 20
local stage = 0
local e_r = rad(45)
local t_s = 1
local function UVMulti(uv,mul)
return (uv - 0.5) * mul + 0.5
end
mesh.Begin( Render_Dome, MATERIAL_TRIANGLES, 24 )
for i = 1,8 do
local yaw = rad(45 * i)
-- Generate the top
-- L
local c,s = cos(yaw),sin(yaw)
local L = {Vector(c * sc,s * sc,0.1 * -sc),(1 + c) / 2 * t_s,(1 + s) / 2 * t_s}
mesh.Position(L[1])
mesh.TexCoord( stage, L[2], L[3])
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
-- R
local c,s = cos(yaw + e_r),sin(yaw + e_r)
local R = {Vector(c * sc,s * sc,0.1 * -sc),(1 + c) / 2 * t_s, (1 + s) / 2 * t_s}
mesh.Position(R[1])
mesh.TexCoord( stage, R[2],R[3] )
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
-- T
mesh.Position(Vector(0,0,0.1 * top_height))
mesh.TexCoord( stage, 0.5 * t_s,0.5 * t_s )
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
-- Generate side1
mesh.Position(L[1])
mesh.TexCoord( stage, L[2], L[3])
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
local R2 = {R[1] * 1.4 - Vector(0,0,4),UVMulti(R[2],1.4),UVMulti(R[3],1.4)}
mesh.Position(R2[1])
mesh.TexCoord( stage, R2[2],R2[3] )
mesh.Color(255,255,255,0)
mesh.AdvanceVertex()
mesh.Position(R[1])
mesh.TexCoord( stage, R[2],R[3] )
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
-- Generate side 2
mesh.Position(L[1])
mesh.TexCoord( stage, L[2], L[3])
mesh.Color(255,255,255,255)
mesh.AdvanceVertex()
mesh.Position(L[1] * 1.4 - Vector(0,0,4))
mesh.TexCoord( stage, UVMulti(L[2], 1.4), UVMulti(L[3],1.4))
mesh.Color(255,255,255,0)
mesh.AdvanceVertex()
mesh.Position(R2[1])
mesh.TexCoord( stage, R2[2],R2[3] )
mesh.Color(255,255,255,0)
mesh.AdvanceVertex()
end
mesh.End()
-- Local functions
local matrix = Matrix()
local function RenderDome(pos,mat,alpha)
matrix:Identity()
matrix:Translate( vector_origin + pos )
--mat:SetAlpha(alpha)
cam.PushModelMatrix(matrix)
render.SetBlend(alpha / 255)
render.SetMaterial(mat)
Render_Dome:Draw()
render.SetBlend(1)
cam.PopModelMatrix()
end
local lastRT
local function RTRender(RT,blend)
lastRT = RT
render.PushRenderTarget( RT )
render.ClearDepth()
render.Clear( 0, 0, 0, 0 )
cam.Start2D()
if not blend then return end
render.OverrideAlphaWriteEnable( true, true )
end
local function RTMask(srcBlend,destBlend,srcBlendAlpha,destBlendAlpha)
local srcBlend = srcBlend or BLEND_ZERO
local destBlend = destBlend or BLEND_SRC_ALPHA --
local blendFunc = 0 -- The blend mode used for drawing the color layer
local srcBlendAlpha = srcBlendAlpha or BLEND_DST_ALPHA -- Determines how a rendered texture's final alpha should be calculated.
local destBlendAlpha = destBlendAlpha or BLEND_ZERO --
local blendFuncAlpha = 0 --
render.OverrideBlend( true, srcBlend, destBlend, blendFunc, srcBlendAlpha, destBlendAlpha, blendFuncAlpha)
end
local function RTEnd(Mat_Output)
render.OverrideBlend( false )
render.OverrideAlphaWriteEnable( false )
cam.End2D()
render.PopRenderTarget()
-- Apply changes
Mat_Output:SetTexture("$basetexture",lastRT)
end
local function DrawTextureRectWindow(w,h,o_x,o_y) -- Render function that supports fractions (surface libary is whole numbers only)
if o_x < 0 then o_x = o_x + w end
if o_y < 0 then o_y = o_y + h end
o_x = o_x % w
o_y = o_y % h
local m = Matrix()
m:Identity()
m:Translate(Vector(o_x % w,o_y % h))
cam.PushModelMatrix(m)
surface.DrawTexturedRect(0,0,w,h)
surface.DrawTexturedRect(-w,0,w,h)
surface.DrawTexturedRect(0,-h,w,h)
surface.DrawTexturedRect(-w,-h,w,h)
cam.PopModelMatrix()
end
-- Load materials
-- Side clouds
local side_clouds = {}
for _,fil in ipairs(file.Find("materials/stormfox2/effects/clouds/side_cloud*.png","GAME")) do
local png = Material("stormfox2/effects/clouds/" .. fil,"nocull noclamp alphatest")
png:SetInt("$flags",2099250)
table.insert(side_clouds,{png,png:GetInt("$realwidth") / png:GetInt("$realheight")})
end
-- Top clouds
local layers = 4
local sky_mats = {}
local offset = {}
local params = {}
params[ "$basetexture" ] = ""
params[ "$translucent" ] = 0
params[ "$vertexalpha" ] = 1
params[ "$vertexcolor" ] = 1
params[ "$nofog" ] = 1
params[ "$nolod" ] = 1
params[ "$nomip" ] = 1
params["$additive"] = 0
for i = 1,layers do
sky_mats[i] = CreateMaterial("StormFox_RTSKY" .. i,"UnlitGeneric",params)
end
local cloudbig = Material("stormfox2/effects/clouds/clouds_big.png","nocull noclamp smooth")
-- 8240
cloudbig:SetInt("$flags",2099250)
cloudbig:SetFloat("$nocull",1)
cloudbig:SetFloat("$nocull",1)
cloudbig:SetFloat("$additive",0)
local sky_rts = {}
local texscale = 512
for i = 1,layers do
sky_rts[i] = GetRenderTargetEx( "StormFox_Sky" .. i, texscale, texscale, 1, MATERIAL_RT_DEPTH_NONE, 2, CREATERENDERTARGETFLAGS_UNFILTERABLE_OK, IMAGE_FORMAT_RGBA8888)
offset[i] = {i * 99,i * 33}
end
local function safeCall(...)
hook.Run("StormFox2.2DSkybox.CloudLayerRender", ...)
end
local function UpdateCloudMaterial(layer,cloud_alpha)
local blend = true
local d_seed = layer * 33
render.PushFilterMag( TEXFILTER.ANISOTROPIC )
render.PushFilterMin( TEXFILTER.ANISOTROPIC )
-- Start RT render
RTRender(sky_rts[layer],blend)
-- Render RT texture
surface.SetMaterial(cloudbig)
surface.SetDrawColor(Color(255,255,255,cloud_alpha))
--surface.DrawTexturedRect(0,0,texscale,texscale)
DrawTextureRectWindow(texscale,texscale,offset[layer][1] + d_seed,offset[layer][2] + d_seed)
-- If we error in here, gmod will crash.
local b, reason = pcall(safeCall, texscale, texscale, layer)
if not b then ErrorNoHalt(reason) end
-- Mask RT tex
-- RTMask()
-- surface.SetDrawColor(Color(255,255,255,255 - cloud_alpha))
-- surface.SetMaterial(cloudbig)
-- DrawTextureRectWindow(texscale,texscale,offset[layer][1] + d_seed,offset[layer][2] + d_seed)
-- End RT tex
RTEnd(sky_mats[layer])
render.PopFilterMag()
render.PopFilterMin()
end
local col = Color(255,255,255,175)
local v = Vector(0,0,-20)
local function RenderCloud(mat_id,yaw,s_size,alpha, pos)
local mat = side_clouds[mat_id]
if not mat then return end
render.SetMaterial(mat[1])
local pitch = 0.11 * s_size
local n = Angle(pitch,yaw,0):Forward()
col.a = math.max(175 * alpha, 255)
render.DrawQuadEasy( n * -200 + pos + v, n, s_size * mat[2] , s_size, col, 180 )
end
local function LerpColor(f, col1, col2)
return Color( Lerp(f, col1.r, col2.r), Lerp(f, col1.g, col2.g), Lerp(f, col1.b, col2.b) )
end
-- Cloud movement
hook.Add("PreRender","StormFox2.Client.CloudMove",function()
local w_ang = rad(StormFox2.Wind.GetYaw())
local w_force = max(StormFox2.Wind.GetForce(),0.1) * 0.08 * RealFrameTime()
local x_w,y_w = cos(w_ang) * w_force,sin(w_ang) * w_force
for i = 1,layers do
local ri = (layers - i + 1)
local x,y = offset[i][1],offset[i][2]
offset[i] = {x + x_w * ri ,y + y_w * ri}
end
end)
hook.Add("StormFox2.2DSkybox.CloudLayer","StormFox2.Client.Clouds",function(eye)
if not StormFox2 then return end
if not StormFox2.Mixer then return end
local cl_amd = StormFox2.Mixer.Get("clouds",0)
--if cl_amd <= 0 then return end
-- Update material-color
local c = StormFox2.Mixer.Get("bottomColor") or Color(204, 255, 255)
-- Render sideclouds
local vec = Vector(c.r,c.g,c.b) / 255
for k,v in ipairs(side_clouds) do
v[1]:SetVector("$color",vec)
end
local cloud_speed = StormFox2.Time.GetSpeed_RAW() * 0.1
local sideclouds = 10 * cl_amd
for i = 1,sideclouds do
local a = 1
if i < sideclouds and i == math.floor(sideclouds) then
a = sideclouds - math.floor(sideclouds)
end
local row = math.floor(i / 3)
local m_id = i % #side_clouds + 1
local y_start = (i % 3) * 120 + row * 33
local size = (3 + i % 5) * 24
RenderCloud(m_id,y_start + i + SysTime() * cloud_speed, size, a, eye * eye_mult * 10 * i / 10 )
end
-- Render top clouds
local up = Vector(0,0,1)
local n = max(0,min(math.ceil(layers * cl_amd),layers))
local thunder = 0
if StormFox2.Thunder then
thunder = min(255,StormFox2.Thunder.GetLight() or 0) / 25
end
for i = 1,n do
local ri = n - i + layers
local cloud_amplifier = 1 + .4 * (1 - (i / n))
if i == 1 then
cloud_amplifier = cloud_amplifier + thunder
end
UpdateCloudMaterial(i,255)
sky_mats[i]:SetVector("$color",Vector(min(c.r * cloud_amplifier,255),min(c.g * cloud_amplifier,255),min(c.b * cloud_amplifier,255)) / 255 )
RenderDome(up * (z_level + 0.4 * ri) + eye * eye_mult,sky_mats[i],255)
end
end)

View File

@@ -0,0 +1,129 @@
--[[
| 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/
--]]
local function niceName(sName)
if sName[1] == "#" then
sName = sName:sub(2)
end
sName = string.Replace(sName, "_", " ")
local str = ""
for s in string.gmatch(sName, "[^%s]+") do
str = str .. string.upper(s[1]) .. string.sub(s, 2) .. " "
end
return string.TrimRight(str, " ")
end
-- SF Settings
local SWin
hook.Add("ContextMenuClosed", "Stormfox2.ContextMC", function()
if not SWin or not IsValid(SWin) then return end
SWin:Remove()
SWin = nil
end)
local setc = Color(55,55,65,255)
local setc2 = Color(255,255,255,55)
local matc = Material("gui/workshop_rocket.png")
local function CreateWindow( icon, window, bAccess )
window:SetTitle("")
window:DockPadding(0, 0, 8, 0)
window:ShowCloseButton(false)
window.c_Time = CurTime() + 0.5
window.c = 0.5
function window:Paint(w,h)
if window.c < 0.99 then
window.c = Lerp( FrameTime() * 10, window.c, 1 )
elseif window.c < 1 then
window.c = 1
end
local f = window.c
surface.SetDrawColor(setc)
surface.SetMaterial(matc)
DisableClipping(true)
surface.DrawTexturedRectUV(-16, 0, 16, h + 2, 0, 0.23, 0.3,0.77)
surface.DrawTexturedRectUV(0, 0,w * f,h + 2, 0.3,0.23, 0.7,0.77)
surface.DrawTexturedRectUV(w * f, 0, 16, h + 2, 0.7,0.23, 1, 0.77)
DisableClipping(false)
end
local cl = vgui.Create("DButton", window)
cl:SetText("")
cl:SetSize(80,82)
cl.Paint = function() end
local cli = vgui.Create("DImage", cl)
cli:SetPos(8,0)
cli:SetSize(64,64)
cli:SetImage("stormfox2/hud/settings.png")
local label = vgui.Create("DLabel", cl )
label:Dock( BOTTOM )
label:SetText( niceName(language.GetPhrase("#client") .. " " .. language.GetPhrase("#superdof_pp.settings")))
label:SetContentAlignment( 5 )
label:SetTextColor( color_white )
label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
label:SizeToContentsX()
local sv = vgui.Create("DButton", window)
sv:SetText("")
sv:SetPos(80,0)
sv:SetSize(80,82)
sv.Paint = function() end
local svi = vgui.Create("DImage", sv)
svi:SetPos(8,0)
svi:SetSize(64,64)
svi:SetImage("stormfox2/hud/controller.png")
local label = vgui.Create("DLabel", sv )
label:Dock( BOTTOM )
label:SetText( niceName(language.GetPhrase("#spawnmenu.utilities.server_settings")))
label:SetContentAlignment( 5 )
label:SetTextColor( color_white )
label:SetExpensiveShadow( 1, Color( 0, 0, 0, 200 ) )
label:SizeToContentsX()
sv.DoClick = function()
surface.PlaySound("buttons/button14.wav")
window:Remove()
StormFox2.Menu.OpenSV()
end
cl.DoClick = function()
surface.PlaySound("buttons/button14.wav")
window:Remove()
StormFox2.Menu.Open()
end
local w,h = icon:LocalToScreen(0,0)
window:SetPos(w,h)
SWin = window
function window:Think()
if self.c_Time > CurTime() then return end
local x,y = self:CursorPos()
if x > 0 and x < self:GetWide() and y > 0 and y < self:GetTall() then return end
self:Remove()
end
if not bAccess then
sv:SetCursor( "no" )
sv:SetDisabled( true )
svi:SetDisabled( true )
label:SetTextColor( Color( 255,255,255, 105) )
sv:SetToolTip(niceName(language.GetPhrase("#administrator_applications")))
end
surface.PlaySound("garrysmod/ui_click.wav")
end
local function OpenWindow(icon, window)
-- We can't check for IsListenServerHost, so lets hope the addminmod does that.
CAMI.PlayerHasAccess(LocalPlayer(), "StormFox Settings",function(bAccess)
CreateWindow( icon, window, bAccess )
end)
end
list.Set( "DesktopWindows", "StormFoxSetting", {
title = "SF " .. niceName(language.GetPhrase("#spawnmenu.utilities.settings")),
icon = "stormfox2/hud/settings.png",
width = 80 * 2,
height = 84,
onewindow = true,
init = OpenWindow
} )

View File

@@ -0,0 +1,204 @@
--[[
| 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/
--]]
-- Breath Efect
do
local threshold = 2 -- IRL it is 7.2C, but i think the community would like to tie it closer to snow.
StormFox2.Setting.AddCL("enable_breath", true)
local m_mats = {(Material("particle/smokesprites_0001")),(Material("particle/smokesprites_0002")),(Material("particle/smokesprites_0003"))}
local function GetMouth( ply )
local att = ply:LookupAttachment("mouth")
if att <= 0 then return end -- No mouth?
return ply:GetAttachment(att)
end
local function DoEffect(ply, size)
if not _STORMFOX_PEM then return end -- Just in case
local pos, ang
local e = 1
if ply ~= StormFox2.util.ViewEntity() then -- "Someone else"
local att = GetMouth( ply )
if not att then return end
pos = att.Pos
ang = att.Ang
else -- Our viewpoint
-- Check the view
local view = StormFox2.util.GetCalcView()
if view.drawviewer then -- Third person
local att = GetMouth( ply )
if not att then return end
pos = att.Pos
ang = att.Ang
else
e = 2
ang = Angle(-view.ang.p,view.ang.y,0)
pos = view.pos + ang:Forward() * 3 + ang:Up() * -2
end
end
local l = StormFox2.Map.GetLight() / 100
local p = _STORMFOX_PEM["2D"]:Add(table.Random(m_mats),pos)
p:SetStartSize(1)
p:SetEndSize(size)
p:SetStartAlpha(math.min(255, 15 + math.random(55,135) * l * e))
p:SetEndAlpha(0)
p:SetLifeTime(0)
p:SetGravity(Vector(0,0,4))
p:SetDieTime(1)
p:SetLighting(false)
p:SetRoll(math.random(360))
p:SetRollDelta(math.Rand(-0.5,0.5))
p:SetVelocity(ang:Forward() * 2 + ply:GetVelocity() / 5)
end
-- Runs the effect on the player and returns next time it should be called.
local function CheckEffect(ply)
if not IsValid( ply) then return end
if not ply:Alive() then return end
if ply:WaterLevel() >= 3 then return end
if ply:InVehicle() then
local e = ply:GetVehicle()
if not IsValid( e ) then return end
if e:GetClass() ~= "prop_vehicle_jeep" then return end -- An open vehicle
end
if not StormFox2.Wind.IsEntityInWind(ply) then return end
local len = ply:GetVelocity():Length()
local t = math.Clamp(1.5 - (len / 100),0.2,1.5)
DoEffect(ply,5 + (len / 100))
return math.Rand(t,t * 2)
end
-- The most optiomal way is to check within the renderhook.
local function RunEffect(ply)
if not StormFox2.Setting.GetCache("enable_breath") then return end
if not StormFox2.Setting.SFEnabled() then return end
if StormFox2.Temperature.Get() > threshold then return end -- Breaht is visible at 7.2C or below
if (ply._sfbreath or 0) >= CurTime() then return end
local cE = CheckEffect( ply )
if not cE then
ply._sfbreath = CurTime() + 1
return
end
ply._sfbreath = CurTime() + cE
end
hook.Add("PostPlayerDraw", "StormFox2.Effect.Breath", RunEffect)
-- We also need to check when the player is in first person.
timer.Create("StormFox2.Effect.BreathT", 1, 0, function()
local LP = LocalPlayer( )
if not IsValid( LP ) then return end
RunEffect( LP )
end)
end
-- Depth Filter
local W,H = ScrW(), ScrH()
local depth_r = GetRenderTarget("SF_DepthFilter", W,H, true)
local depthLayer = Material( "stormfox2/shader/depth_layer" )
local a = 0
local l
-- Patch: Some people out there don't update the resources when updating the mod.
if depthLayer:GetKeyValues()["$detail"] then
StormFox2.Warning("stormfox2/shader/depth_layer.vmt is outdated! Hotpatching, but be sure to update the resources!")
depthLayer:SetUndefined("$detail")
depthLayer:SetUndefined("$detailtexturetransform")
depthLayer:SetUndefined("$detailblendmode")
depthLayer:SetUndefined("$detailscale")
depthLayer:SetUndefined("$additive")
end
hook.Add("StormFox2.weather.postchange", "StormFox2.DepthFilter.Reset", function(b)
if l and l == b then return end
l = b
a = 0
end)
local depthLayer = Material( "stormfox2/shader/depth_layer" )
local function updateDepthTexture(fFunc, a)
render.PushRenderTarget(depth_r)
cam.Start2D()
render.Clear(0,0,0,0,true, false)
fFunc(W,H, a)
cam.End2D()
render.PopRenderTarget()
depthLayer:SetTexture("$basetexture", depth_r)
return depthLayer
end
local invis_col = Color(255,0,0,0)
local function RenderDepthFilter()
-- Reset everything to known good fpr stencils
render.SetStencilWriteMask( 0xFF )
render.SetStencilTestMask( 0xFF )
render.SetStencilReferenceValue( 0 )
render.SetStencilCompareFunction( STENCIL_ALWAYS )
render.SetStencilPassOperation( STENCIL_REPLACE )
render.SetStencilFailOperation( STENCIL_ZERO )
render.SetStencilZFailOperation( STENCIL_ZERO )
render.ClearStencil()
-- Enable stencil
render.SetStencilEnable( true )
render.SetStencilReferenceValue( 1 )
render.SetStencilCompareFunction( STENCIL_ALWAYS )
-- Render Mask
local eA = EyeAngles()
cam.Start3D( EyePos(), eA )
render.SetColorMaterial()
local f_D = StormFox2.Fog.GetEnd()
if f_D > 2000 then
render.DrawSphere(EyePos(), -2000, 30, 30, invis_col)
else
--[[
Stencils look bad for heavy effects, since there is a clear pixel-line.
I've tried smoothing it, but it would require rendering the world twice within an RT.
So instead we make it a plain with the fog-distance.
Its bad in some cases, yes, but only solution I know for now.
]]
render.DrawQuadEasy(EyePos() + eA:Forward() * f_D, -eA:Forward(), ScrW() * 5, ScrH() * 5, invis_col, 0)
end
cam.End3D()
-- Now, only draw things that have their pixels set to 1. This is the hidden parts of the stencil tests.
render.SetStencilCompareFunction( STENCIL_EQUAL )
-- Render Depth-filter
cam.Start2D()
render.SetMaterial(depthLayer)
render.DrawScreenQuad()
cam.End2D()
-- Let everything render normally again
render.SetStencilEnable( false )
--render.PopRenderTarget()
end
hook.Add("RenderScreenspaceEffects", "StormFox2.Downfall.DepthRender", function()
if render.GetDXLevel() < 95 then return end
if not render.SupportsPixelShaders_2_0() then return end
if LocalPlayer():WaterLevel() >= 3 then return end -- Don't render SF effects under wanter.
local obj = StormFox2.Setting.GetObject("depthfilter")
if not obj then return end
if not obj:GetValue() then return end
-- Check if weather has depth. If not reset alpha
local dFr = StormFox2.Weather.GetCurrent().DepthFilter
if not dFr then a = 0 return end
-- Calc alpha
if StormFox2.Environment.Get().outside then
a = math.Approach(a, 1, FrameTime() * .8)
else
a = math.Approach(a, 0, FrameTime() * 5) -- Quick fadeaway
end
if a <= 0 then return end -- If alpha is 0 or below, don't render.
-- Update RT
updateDepthTexture(dFr, a)
-- Update screenspace effect
render.UpdateScreenEffectTexture()
render.UpdateFullScreenDepthTexture()
-- Render the depthfilter
RenderDepthFilter()
end)

View File

@@ -0,0 +1,269 @@
--[[
| 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/
--]]
-- This is a localization backup, in case the file didn't transferre.
if not file.Exists("resource/localization/en/stormfox.properties", "GAME") then return end
local str = [[
#StormFox2.weather = weather
# https://github.com/Facepunch/garrysmod/blob/master/garrysmod/resource/localization/en/community.properties Check this first
# weather, day, night, sun, lightning, snow, cloud
#Misc
sf_auto=Auto
sf_customization=Customization
sf_window_effects=Window Effects
footprints=footprints
temperature=temperature
fog=Fog
#Weather
sf_weather.clear=Clear
sf_weather.clear.windy=Windy
sf_weather.cloud=Cloudy
sf_weather.cloud.thunder=Thunder
sf_weather.cloud.storm=Storm
sf_weather.rain=Raining
sf_weather.rain.sleet=Sleet
sf_weather.rain.snow=Snowing
sf_weather.rain.thunder=Thunder
sf_weather.rain.storm=Storm
sf_weather.fog.low=Haze
sf_weather.fog.medium=Fog
sf_weather.fog.high=Thick Fog
sf_weather.lava=Lava
sf_weather.fallout=Nuclear Fallout
#Tool
sf_tool.name=StormFox2 Tool
sf_tool.desc=Allows you to edit StormFox2 map settings.
sf_tool.surface_editor=Surface Editor
sf_tool.surface_editor.desc=Allows you to edit surface-types.
sf_tool.surface_editor.entertext=Write the texture path and click add.
sf_tool.light_editor=Light Editor
sf_tool.light_editor.desc=Allows you to add/remove sf-lights.
sf_enable_breath=Enables breath
sf_enable_breath.desc=Makes players breath visible in cold.
#Settings
sf_enable=Enable StormFox
sf_enable.desc=Enable / Disable StormFox 2
sf_clenable=Enable StormFox
sf_clenable.desc=Enable / Disable StormFox 2. Requires sf_allow_csenable.
sf_allow_csenable=Allow Clients to disable StormFox 2
sf_allow_csenable.desc=Enabling this will allow clients to disable StormFox 2.
sf_mthreadwarning=These settings can boost your FPS:\n%s\nWarning\: This Might crash on some older AMD CPUs!
sf_holdc=Hold C
sf_weatherpercent=Weather Amount
sf_setang=Set Angle
sf_setang.desc=Sets the wind-angle to your view.
sf_setwind=Sets the windspeed in m/s
sf_wcontoller=SF Controller
sf_map.light_environment.check=This map support fast lightchanges.
sf_map.light_environment.problem=This map will cause lagspikes for clients, when the light changes.
sf_map.env_wind.none=This map doesn't support windgusts.
sf_map.logic_relay.check=This map has custom day/night relays.
sf_map.logic_relay.none=This map doens't have custom day/night relays.
sf_windmove_players=Affect players
sf_windmove_players.desc=Affect player movment in strong wind.
sf_windmove_foliate=Affect Foliate
sf_windmove_foliate.desc=Foliate moves with the wind.
sf_windmove_props=Affect Props
sf_windmove_props.desc=Props will move with the wind. This can cause lag!
sf_windmove_props_break=Damage Props
sf_windmove_props_break.desc=Props will take damage in the wind.
sf_windmove_props_makedebris=Change CollisionGroup
sf_windmove_props_makedebris.desc=Will make props change collisiongroup, reducing lag.
sf_windmove_props_unfreeze=Unfreeze props.
sf_windmove_props_unfreeze.desc=Unfreeze props being moved by the wind.
sf_windmove_props_unweld=Unweld props.
sf_windmove_props_unweld.desc=Unweld props being moved by the wind.
sf_windmove_props_max=Max props being moved.
sf_windmove_props_max.desc=Max amount of props moving. This can cause lag!
sf_enable_fogz=Overwrite farZ fog
sf_enable_fogz.desc=Overwrites the maps farZ fog. This might look bad on some maps.
sf_enable_ice=Enable ice
sf_enable_ice.desc=Creates ice over water.
sf_overwrite_fogdistance=Default fog-distance.
sf_overwrite_fogdistance.desc=Overwrites the default fog-distance.
sf_hide_forecast=Hide Forecast
sf_hide_forecast.desc=Stops clients from updating the forecast.
sf_allow_weather_lightchange=Allow weather maplight
sf_allow_weather_lightchange.desc=Allows the weather to modify the maplight.
sf_addnight_temp=Add Night Temperature
sf_addnight_temp.desc=The amount the temperature drops doing night.
sf_apply_settings=Apply settings.
sf_reset_settings=Reset settings.
sf_enable_skybox=Enable Skybox
sf_enable_skybox.desc=Allows StormFox to use the skybox.
sf_use_2dskybox=Use 2D Skybox
sf_use_2dskybox.desc=Makes StormFox use 2D skyboxes instead.
sf_overwrite_2dskybox=Overwrite 2D skybox
sf_overwrite_2dskybox.desc=Overwrites the 2D skybox with another.
sf_darken_2dskybox=Darken 2D Skybox
sf_darken_2dskybox.desc=Match the skybox brightness with the map.
sf_random_round_weather=Random weather each round.
sf_random_round_weather.desc=Gamemodes like TTT will have random weathers between each round.
sf_quickselect=Quick Select.
sf_quickselect.desc=Quick select time settings.
sf_depthfilter=Depth Filter
sf_depthfilter.desc=Render 2D weather-effects on clients screen.
sf_enable_sunbeams=Enable Sunbeams
sf_enable_sunbeams.desc=Enable sunbeams when the sun is low.
sf_edit_cubemaps=Edit Cubemaps
sf_edit_cubemaps.desc=Change cubemap tint of map-materials.
#Details
sf_quality_target=FPS Target
sf_quality_target.desc=Adjusts the quality to reach targeted FPS.
sf_quality_ultra=Ultra Quality
sf_quality_ultra.desc=Allows for more effects.
sf_12h_display=Time Display
sf_12h_display.disc=Choose 12-hour or 24-hour clock.
sf_display_temperature=Temperature Units
sf_display_temperature.desc=Choose a temperature unit.
sf_use_monthday=Date Display
sf_use_monthday.disc=Choose MM/DD or DD/MM.
#Wind
sf_wind=Wind
sf_winddescription.calm=Calm
sf_winddescription.light_air=Light Air
sf_winddescription.light_breeze=Light Breeze
sf_winddescription.gentle_breeze=Gentle Breeze
sf_winddescription.moderate_breeze=Moderate Breeze
sf_winddescription.fresh_breeze=Fresh Breeze
sf_winddescription.strong_breeze=Strong Breeze
sf_winddescription.near_gale=Near Gale
sf_winddescription.gale=Gale
sf_winddescription.strong_gale=Strong Gale
sf_winddescription.storm=Storm
sf_winddescription.violent_storm=Violent Storm
#Hurricane is also known as Category 1
sf_winddescription.hurricane=Hurricane
sf_winddescription.cat2=Category 2
sf_winddescription.cat3=Category 3
sf_winddescription.cat4=Category 4
sf_winddescription.cat5=Category 5
#Time
sf_continue_time=Continuous Time
sf_continue_time.desc=Continue time from last time.
sf_real_time=Real time
sf_real_time.desc=Use the servers OS Time.
sf_start_time=Start time
sf_start_time.desc=Sets the start time.
sf_random_time=Random time
sf_random_time.desc=Sets the time randomly on server-launch.
sf_day_length=Day Length
sf_day_length.desc=How long the day is in minutes.
sf_night_length=Night Length
sf_night_length.desc=How long the night is in minutes.
#Sun
sf_sunrise=SunRise
sf_sunrise.desc=Sets the time the sun rises.
sf_sunset=SunSet
sf_sunset.desc=Sets the time the sun sets.
sf_sunyaw=SunYaw
sf_sunyaw.desc=Sets the yaw for the sun.
#Moon
sf_moonsize=Moon Size
sf_moonsize.desc=The default moon size.
sf_moonphase=Moon Phases
sf_moonphase.desc=Enable Moon Phases.
sf_moonlock=Moon Lock
sf_moonlock.desc=Locks the moon to the sun's rotation.
#'Maplight
sf_maplight_max=Max Maplight
sf_maplight_max.desc=The max lightlevel. You can adjust this if the map is too bright/dark.
sf_maplight_min=Min Maplight
sf_maplight_min.desc=The min lightlevel. You can adjust this if the map is too bright/dark.
sf_maplight_smooth=Maplight Lerp.
sf_maplight_smooth.desc=Enables smooth light transitions.
sf_maplight_updaterate=Maplight UpdateRate
sf_maplight_updaterate.desc=The max amount of times StormFox will update the maplight doing transitions. Will cause lag on large maps!
sf_maplight_auto.desc=Select the best/fastes option for the map.
sf_maplight_lightenv.desc=Enable light_environment.
sf_maplight_colormod.desc=Enable colormod.
sf_maplight_dynamic.desc=Enable dynamic light/shadows.
sf_maplight_lightstyle.desc=Enable lightstyle.
sf_modifyshadows=Modify shadows
sf_modifyshadows.desc=Modify default shadows to follow the sun.
sf_modifyshadows_rate=Modify shadow rate
sf_modifyshadows_rate.desc=The seconds between each shadow-update.
sf_extra_lightsupport=Extra Lightsupport
sf_extra_lightsupport.desc=Utilize engine.LightStyle to change the map-light. This can cause lag-spikes, but required on certain maps.
#Effects
sf_enable_fog=Enable Fog
sf_enable_fog.desc=Allow StormFox to edit the fog.
sf_allow_fog_change=Allow clients to toggle fog.
sf_allow_fog_change.desc=Enabling this will allow clients to toggle fog.
sf_footprint_enabled=Enable Footprints
sf_footprint_enabled.desc=Enable footprint effects.
sf_footprint_playeronly=Player Footprints Only.
sf_footprint_playeronly.desc=Only players make footprints.
sf_footprint_distance=Footprint Render Distance
sf_footprint_distance.desc=Max render distance for footprints.
sf_footprint_max=Max Footprints
sf_footprint_max.desc=Max amount of footprints
sf_edit_tonemap=Enable tonemap
sf_edit_tonemap.desc=Allow StormFox to edit the tonemap.
sf_enable_wateroverlay=Render water overlay
sf_enable_wateroverlay.desc=Enables water-overlay for weather-types.
sf_extra_darkness=Extra Darkness
sf_extra_darkness.desc=Adds a darkness-filter to make bright maps darker.
sf_extra_darkness_amount=Extra Darkness Amount
sf_extra_darkness_amount.desc=Scales the darkness-filter.
sf_overwrite_extra_darkness=Overwrite Extra Darkness
sf_overwrite_extra_darkness.desc=Overwrites the players sf_extra_darkness.
sf_footprint_enablelogic=Enables Serverside Footprints
sf_footprint_enablelogic.desc=Enables server-side footprints.
sf_window_enable=Enable window effects
sf_window_enable.desc=Enables window weather effects.
sf_window_distance=Window Render Distance
sf_window_distance.desc=The render distance for breakable windows.
sf_override_foliagesway=Override Foliagesway
sf_override_foliagesway.desc=Overrides and applies foliagesway to most foliage on launch.
#Weather
sf_auto_weather=Auto weather
sf_auto_weather.desc=Automatically change weather over time.
sf_max_weathers_prweek=Max Weathers Pr Week
sf_max_weathers_prweek.desc=Max amount of weathers pr week.
sf_temp_range=Temperature range
sf_temp_range.desc=The min and max temperature.
sf_temp_acc=Temperature change.
sf_temp_acc.desc=The max temperature changes pr day.
sf_weather_damage=Weather Damage
sf_weather_damage.desc=Allow weather to cause damage
sf_max_wind=Maximum wind
sf_max_wind.desc=The maximum generated wind in m/s.
]]
for k, v in ipairs( string.Explode("\n", str)) do
if string.match(v, "%s-#") then continue end
local a,b = string.match(v, "%s*(.+)=(.+)")
if not a or not b then continue end
language.Add(a, b)
end

View File

@@ -0,0 +1,334 @@
--[[
| 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.Menu = StormFox2.Menu or {}
local function niceName(sName)
if sName[1] == "#" then
sName = sName:sub(2)
end
sName = string.Replace(sName, "_", " ")
local str = ""
for s in string.gmatch(sName, "[^%s]+") do
str = str .. string.upper(s[1]) .. string.sub(s, 2) .. " "
end
return string.TrimRight(str, " ")
end
local function wrapText(sText, wide)
wide = wide - 10
local tw,th = surface.GetTextSize(language.GetPhrase(sText))
local lines,b = 1, false
local s = ""
for w in string.gmatch(sText, "[^%s,]+") do
local tt = s .. (b and " " or "") .. w
if surface.GetTextSize(tt) >= wide then
s = s .. "\n" .. w
lines = lines + 1
else
s = tt
end
b = true
end
return s, lines
end
local m = Material("gui/gradient")
local function paintKnob(self,x, y) -- Skin doesn't have x or y pos
local skin = self:GetSkin()
if ( self:GetDisabled() ) then return skin.tex.Input.Slider.H.Disabled( x, y, 15, 15 ) end
if ( self.Depressed ) then
return skin.tex.Input.Slider.H.Down( x, y, 15, 15 )
end
if ( self.Hovered ) then
return skin.tex.Input.Slider.H.Hover( x, y, 15, 15 )
end
skin.tex.Input.Slider.H.Normal( x, y, 15, 15 )
end
local notchesColor = Color(0,0,0,100)
-- Tips
local function AddTip( self, text )
if IsValid( self.tTip ) then return end
self.tTip = vgui.Create("DTooltip")
self.tTip:SetText( text )
self.tTip.TargetPanel = self
self.tTip:PositionTooltip()
end
local function RemoveTip( self )
if IsValid( self.tTip ) then
self.tTip:Remove()
end
self.tTip = nil
end
local tabs = {
[1] = {"Start","#start",(Material("stormfox2/hud/menu/dashboard.png")),function(board)
board:AddTitle("#information")
local dash = vgui.Create("DPanel", board)
dash.Paint = empty
dash:Dock(TOP)
dash:SetTall(80)
local fps, qu, sup, mth
-- FPS
local p = vgui.Create("SF_Setting_Ring", dash)
p:SetText(string.upper(language.GetPhrase("#fps")) .. ": ")
p:SetSize(74, 74)
p:SetPos(24,10)
function p:Think()
if (self.u_t or 0) > SysTime() then return end
if not system.HasFocus() then return end
self.u_t = SysTime() + 1
local t = StormFox2.Setting.GetCache("quality_target",144)
local _, avgFPS = StormFox2.Client.GetQualityNumber()
self:SetValue( avgFPS / t)
p:SetText(string.upper(language.GetPhrase("#fps")) .. ": " .. math.floor(avgFPS))
end
fps = p
-- Quality
local p = vgui.Create("SF_Setting_Ring", dash)
p:SetText(language.GetPhrase("#effects"))
p:SetSize(74, 74)
p:SetPos(106,10)
function p:Think()
if (self.u_t or 0) > SysTime() then return end
if not system.HasFocus() then return end
self.u_t = SysTime() + 1
local max_q = StormFox2.Setting.GetCache("quality_ultra",false) and 20 or 7
local q, _ = StormFox2.Client.GetQualityNumber()
local f = q / max_q
self:SetValue( f )
p:SetText(language.GetPhrase("#effects") .. "\n" .. math.floor(f * 100) .. "%")
end
qu = p
-- Support GPU
local p = vgui.Create("SF_Setting_Ring", dash)
p:SetText(niceName(language.GetPhrase("#support")))
p:SetSize(74, 74)
p:SetPos(188,10)
--p:SetColor(255,0,0)
local t = {render.SupportsPixelShaders_1_4(),render.SupportsVertexShaders_2_0(), render.SupportsPixelShaders_2_0(), render.SupportsHDR()}
local v = 0
local s ="#problems.no_problems"
for k,v2 in ipairs(t) do
if not v2 then
if k == 1 then
s = "#problem.no_ps14"
elseif k == 2 then
s = "#problem.no_vs20"
elseif k == 3 then
s = "#problem.no_ps20"
else
s = "#problem.no_hdr"
end
break
end
v = v + 1
end
p:SetTooltip(s)
local f = v / #t
p:SetValue(f)
local c = HSLToColor(120 * f, 1, 0.5 * f)
--p:SetColor(c.r,c.g,c.b)
p:SetText(niceName(language.GetPhrase("#support")) .. "\n" .. v .. "/" .. #t)
sup = p
-- MThread
local p = vgui.Create("SF_Setting_Ring", dash)
p:SetText(niceName(language.GetPhrase("#MThread")))
p:SetSize(74, 74)
p:SetPos(188,10)
--p:SetColor(255,0,0)
local t = {["cl_threaded_bone_setup"] = 1,
["cl_threaded_client_leaf_system"] = 1,
["r_threaded_client_shadow_manager"] = 1,
["r_threaded_particles"] = 1,
["r_threaded_renderables"] = 1,
["r_queued_ropes"] = 1,
["studio_queue_mode"] = 1,
["gmod_mcore_test"] = 1,
["mat_queue_mode"] = 2}
local v = 0
local s = "\n"
for k,v2 in pairs(t) do
if GetConVar(k):GetInt() ~= v2 then
s = s .. k .. " " .. v2 .. "\n"
continue
end
v = v + 1
end
local f = v / table.Count(t)
p:SetValue(f)
local c = HSLToColor(120 * f, 1, 0.5 * f)
--p:SetColor(c.r,c.g,c.b)
p:SetText(niceName(language.GetPhrase("#MThread")) .. "\n" .. v .. "/" .. table.Count(t))
if f < 1 then
p:SetTooltip(string.format(language.GetPhrase("#sf_mthreadwarning"), s))
else
p:SetTooltip(language.GetPhrase("#problems.no_problems"))
end
mth = p
function dash:PerformLayout(w, h)
local a = w / 5
local x = -fps:GetTall() / 2
fps:SetPos(x + a, h - fps:GetTall())
qu:SetPos(x + a*2, h - qu:GetTall())
sup:SetPos(x + a*3, h - sup:GetTall())
mth:SetPos(x + a*4, h - sup:GetTall())
end
-- Fps Slider
local FPSTarget = vgui.Create("SF_Setting", board)
FPSTarget:SetSetting("quality_target")
board:MarkUsed("quality_target")
do
local obj = StormFox2.Setting.GetObject("quality_target")
local slider = vgui.Create("DButton", FPSTarget)
local text = vgui.Create("DTextEntry", FPSTarget)
FPSTarget:MoveDescription(340)
slider:SetPos(5,15)
slider:SetSize( 300, 25 )
slider:SetText("")
text:SetPos( 304, 19)
text:SetSize( 40,20 )
local hot = Color(255,0,0,205)
-- Text set
function text:OnEnter( str )
str = str:lower()
if str == language.GetPhrase("#all"):lower() or str == "all" then
str = 0
else
str = tonumber( str ) or 0
end
obj:SetValue(str)
end
-- Slider skin functions
function slider:GetNotchColor()
return notchesColor
end
function slider:GetNotches()
return math.floor(self:GetWide() / 21)
end
-- Slider paint
function slider:Paint( w, h )
local var = self._OvR or StormFox2.Setting.GetCache("quality_target", 144)
local cV = 300 - var
local skin = self:GetSkin()
skin:PaintNumSlider(self,w,h)
-- "warm"
surface.SetMaterial(m)
surface.SetDrawColor(hot)
local wi = w / 300 * 260
surface.DrawTexturedRectUV(wi - 7, 4, w - wi, h - 6, 1,0,0,1)
paintKnob(self, 1 + (w - 16) / 300 * cV,-0.5)
end
function slider:UpdateText( var )
if var > 0 then
text:SetText(var)
else
text:SetText(niceName(language.GetPhrase("#all")))
end
end
-- Slider think
function slider:Think()
if self:IsDown() then
self._down = true
self._OvR = math.Clamp(1 - (self:LocalCursorPos() - 6) / (self:GetWide() - 12), 0, 1) * 300
if self._OvR < 40 then
AddTip(self, "#frame_blend_pp.desc2")
else
RemoveTip( self )
end
self:UpdateText( math.Round(self._OvR, 0) )
else
if not text:IsEditing() then
self:UpdateText( math.Round(obj:GetValue(), 0) )
end
self._OvR = nil
RemoveTip( self )
if self._down then
self._down = nil
local var = math.Clamp(1 - (self:LocalCursorPos() - 6) / (self:GetWide() - 12), 0, 1) * 300
obj:SetValue( math.Round(var, 0) )
end
end
end
slider:UpdateText(math.Round(obj:GetValue(), 0))
end
FPSTarget:Dock(TOP)
-- EnableDisable
local p = board:AddSetting("clenable")
--local qs = board:AddSetting("quality_target")
board:AddSetting("quality_ultra")
board:AddTitle("#sf_customization")
local l = vgui.Create("DPanel", board)
l:DockMargin(10,0,0,0)
l:SetTall(24)
l:Dock(TOP)
function l:Paint(w,h)
local md = StormFox2.Setting.GetCache("use_monthday",false) and os.date( "%m/%d/%Y" ) or os.date( "%d/%m/%Y" )
local dt = StormFox2.Setting.GetCache("display_temperature")
local hs = string.Explode(":", os.date( "%H:%M") or "17:23")
local n = hs[1] * 60 + hs[2]
local str = niceName(language.GetPhrase("#time")) .. ": " .. StormFox2.Time.GetDisplay(n) .. " " .. md
str = str .. " " .. niceName(language.GetPhrase("#temperature")) .. ": " .. math.Round(StormFox2.Temperature.Convert(nil,dt,22), 1) .. StormFox2.Temperature.GetDisplaySymbol()
draw.DrawText(str, "DermaDefaultBold", 5, 0, color_black, TEXT_ALIGN_LEFT)
end
board:AddSetting("12h_display")
board:AddSetting("use_monthday")
board:AddSetting("display_temperature")
end},
[2] = {"Effects","#effects",(Material("stormfox2/hud/menu/settings.png")),function(board)
board:AddTitle(language.GetPhrase("#effects"))
local fog = board:AddSetting("enable_fog")
board:AddSetting("extra_darkness")
board:AddSetting("extra_darkness_amount")
board:AddSetting("enable_breath")
board:AddSetting("enable_sunbeams")
board:AddSetting("edit_cubemaps")
board:AddTitle(language.GetPhrase("#footprints"))
board:AddSetting("footprint_enabled")
board:AddSetting("footprint_playeronly")
board:AddSetting("footprint_distance")
board:AddSetting("footprint_max")
board:AddTitle(language.GetPhrase("#sf_window_effects"))
board:AddSetting("window_enable")
board:AddSetting("window_distance")
fog:SetDisabled(not StormFox2.Setting.GetCache("allow_fog_change"))
StormFox2.Setting.Callback("allow_fog_change",function(vVar,_,_, self)
fog:SetDisabled(not vVar)
end,fog)
end},
[3] = {"Misc","#misc",(Material("stormfox2/hud/menu/other.png")),function(board)
board:AddTitle("SF2 " .. language.GetPhrase("spawnmenu.utilities.settings"))
local panel = board:AddSetting("mapfile_cl")
panel:SetTitle("#makepersistent")
panel:SetDescription(language.GetPhrase("#persistent_mode") .. " data\\stormfox2\\cl_settings\\" .. game.GetMap() .. ".json")
end},
[4] = {"DLC","DLC",(Material("stormfox2/hud/menu/dlc.png")), function(board)
hook.Run("stormfox2.menu.dlc", board)
end}
}
---Opens the client-settings.
---@client
function StormFox2.Menu.Open()
if _SFMENU and IsValid(_SFMENU) then
_SFMENU:Remove()
_SFMENU = nil
end
local p = vgui.Create("SF_Menu")
_SFMENU = p
p:SetTitle("StormFox " .. niceName(language.GetPhrase("#client")) .. " ".. language.GetPhrase("#spawnmenu.utilities.settings"))
p:CreateLayout(tabs, StormFox2.Setting.GetAllClient())
p:SetCookie("sf2_lastmenucl")
_SFMENU:MakePopup()
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
--[[
| 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/
--]]
--[[<Ignore All>-------------------------------------------------------------------------
We overwrite the sky variables. Its much better to handle it clientside.
---------------------------------------------------------------------------]]
-- Override skypaint- Since its set by each tick.
local g_SkyPaint_tab = {}
function g_SkyPaint_tab.IsValid() return true end
local g_datacache = {}
function g_SkyPaint_tab:GetNetworkVars()
return g_datacache
end
-- Setup data
local function AddDataCache(name,defaultdata)
g_datacache[name] = defaultdata
g_SkyPaint_tab["Get" .. name] = function()
return g_datacache[name]
end
g_SkyPaint_tab["Set" .. name] = function(self,var)
g_datacache[name] = var or defaultdata
end
return g_SkyPaint_tab["Set" .. name]
end
_STORMFOX_TOPCOLOROR = AddDataCache("TopColor", Vector( 0.2, 0.5, 1.0 ) )
AddDataCache("BottomColor", Vector( 0.8, 1.0, 1.0 ) )
AddDataCache("FadeBias", 1 )
AddDataCache("SunNormal", Vector( 0.4, 0.0, 0.01 ) )
AddDataCache("SunColor", Vector( 0.2, 0.1, 0.0 ) )
AddDataCache("SunSize", 2.0 )
AddDataCache("DuskColor", Vector( 1.0, 0.2, 0.0 ) )
AddDataCache("DuskScale", 1 )
AddDataCache("DuskIntensity", 1 )
AddDataCache("DrawStars", true )
AddDataCache("StarLayers", 1 )
AddDataCache("StarSpeed", 0.01 )
AddDataCache("StarScale", 0.5 )
AddDataCache("StarFade", 1.5 )
AddDataCache("StarTexture", "skybox/starfield" )
AddDataCache("HDRScale", 0.66 )
-- Override the skypaint directly
local SkyPaintEnt
local c = false
if #ents.FindByClass("env_skypaint") > 0 then
SkyPaintEnt = ents.FindByClass("env_skypaint")[1]
end
hook.Add("Think","StormFox2.sky.paintFix",function()
if not IsValid(g_SkyPaint) then return end
-- Disable skybox and reset entity
if not StormFox2.Setting.GetCache("enable_skybox", true) or not StormFox2.Setting.SFEnabled() then
if SkyPaintEnt and type(g_SkyPaint) ~= "Entity" then
g_SkyPaint = SkyPaintEnt
c = false
end
return
end
if type(g_SkyPaint) ~= "Entity" then
return
end
if g_SkyPaint:GetClass() == "env_skypaint" then
-- We'll hande it from here
SkyPaintEnt = g_SkyPaint
g_SkyPaint = g_SkyPaint_tab
c = true
end
end)
-- Local functions
local min,max,abs,app = math.min,math.max,math.abs,math.Approach
local function ColVec(col,div)
if not div then
return Vector(col.r,col.g,col.b)
end
return Vector(col.r / div,col.g / div,col.b / div)
end
-- Read and set the skydata
hook.Add("Think","StormFox2.sky.think",function()
if not IsValid(g_SkyPaint) then return end
if not StormFox2.Time then return end
if not StormFox2.Mixer then return end
if not StormFox2.Setting.SFEnabled() then return end
if not StormFox2.Setting.GetCache("enable_skybox", true) then return end
if StormFox2.Setting.GetCache("use_2dskybox",false,nil, "Effects") then return end
if not c then return end -- Make sure we use the table, and not the entity.
-- Top color + Thunder
local fogAm
if StormFox2.Fog then
fogAm = StormFox2.Fog.GetAmount()
end
local thunder = 0
if StormFox2.Thunder then
local cl_amd = StormFox2.Mixer.Get("clouds",0) or 0
thunder = min(255,StormFox2.Thunder.GetLight() or 0) * 0.1 + (cl_amd * .9)
end
local t_data = StormFox2.Mixer.Get("topColor") or Color( 51, 127.5, 255 )
local t_color = Color(max(thunder,t_data.r),max(thunder,t_data.g),max(thunder,t_data.b))
local b_color = StormFox2.Mixer.Get("bottomColor") or Color(204, 255, 255)
if fogAm and fogAm > 0.75 then
t_color = StormFox2.Mixer.Blender((fogAm - .75) * 3, t_color, StormFox2.Fog.GetColor())
end
g_SkyPaint:SetTopColor(ColVec(t_color,255))
g_SkyPaint:SetBottomColor(ColVec(b_color,255))
g_SkyPaint:SetFadeBias(StormFox2.Mixer.Get("fadeBias",0.2))
g_SkyPaint:SetDuskColor(ColVec(StormFox2.Mixer.Get("duskColor",color_white) or color_white,255))
g_SkyPaint:SetDuskIntensity(StormFox2.Mixer.Get("duskIntensity",1.94))
g_SkyPaint:SetDuskScale(StormFox2.Mixer.Get("duskScale",0.29))
-- Stars
local n = StormFox2.Mixer.Get("starFade",100) * 0.015
if n <= 0 then
g_SkyPaint:SetDrawStars(false)
g_SkyPaint:SetStarFade(0)
else
g_SkyPaint:SetDrawStars(true)
g_SkyPaint:SetStarSpeed((StormFox2.Mixer.Get("starSpeed") or 0.001) * StormFox2.Time.GetSpeed_RAW())
g_SkyPaint:SetStarFade(n)
g_SkyPaint:SetStarScale(StormFox2.Mixer.Get("starScale") or 0.5)
g_SkyPaint:SetStarTexture(StormFox2.Mixer.Get("starTexture","skybox/starfield"))
end
-- SunSize
local s_size = StormFox2.Mixer.Get("sunSize",2) * (StormFox2.Mixer.Get("skyVisibility",100) / 100)
g_SkyPaint:SetSunSize(s_size / 10)
if StormFox2.Sun and StormFox2.Sun.GetAngle then
g_SkyPaint:SetSunNormal(StormFox2.Sun.GetAngle():Forward())
local sF = StormFox2.Mixer.Get("sunFade", 1)
g_SkyPaint:SetSunColor(ColVec(StormFox2.Mixer.Get("sunColor"), 1550 / sF))
end
g_SkyPaint:SetHDRScale(StormFox2.Mixer.Get("HDRScale",0.7))
end)
-- Debug
if true then return end
local x = 0
local x2 = 0
local function drawVal(text, val)
if type(val) == "table" then
val = val.r .. " " .. val.g .. " " .. val.b
end
draw.DrawText(text .. ": " .. tostring(val), "DermaDefault", x2, x * 20, color_white, TEXT_ALIGN_LEFT)
x = x + 1
end
hook.Add("HUDPaint", "SF_DEBUG.Sky", function()
local t_color = StormFox2.Mixer.Get("topColor") or Color( 51, 127.5, 255 )
local b_color = StormFox2.Mixer.Get("bottomColor") or Color(204, 255, 255)
x = 1
x2 = 10
drawVal("StormFox2 Debug","")
x2 = 20
drawVal("TopColor",t_color)
drawVal("BottomColor",t_color)
drawVal("fadeBias",StormFox2.Mixer.Get("fadeBias",0.2))
drawVal("duskIntensity",StormFox2.Mixer.Get("duskIntensity",1.94))
drawVal("duskScale",StormFox2.Mixer.Get("duskScale",0.29))
drawVal("starFade",StormFox2.Mixer.Get("starFade",100))
drawVal("starScale",StormFox2.Mixer.Get("starScale",0.5))
drawVal("starSpeed",StormFox2.Mixer.Get("starSpeed",0.001))
drawVal("starTexture",StormFox2.Mixer.Get("starTexture","skybox/starfield"))
end)

View File

@@ -0,0 +1,33 @@
--[[
| 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/
--]]
local s = string.Explode("-", os.date("%m-%d"))
if s[1] ~= "10" then return end
if tonumber(s[2]) < 20 then return end
-- Layers are large by design. (Kinda like a fish lense). Center is "large".
local mat = Material("hud/killicons/default")
local c = Color(255,255,255,0)
local dist = 80 -- 80 to 60
local size = 32 -- 64 to 32
hook.Add("StormFox2.2DSkybox.CloudLayerRender", "StormFox2.IAmNotHere", function(w, h, layer)
local d = StormFox2.Date.GetYearDay()
if d % 2 == 1 then return end
if layer ~= 1 then return end
local rotate = d * 33 % 360
local p = StormFox2.Weather.GetPercent()
c.a = math.min(105, (p - 0.1) * 1000)
if c.a <= 0 then return end
local x, y, ang = math.cos(math.rad(rotate)) * dist, math.sin(math.rad(rotate)) * dist, t
surface.SetDrawColor(c)
surface.SetMaterial(mat)
surface.DrawTexturedRectRotated(w / 2 + x,h / 2 + y, size,size, 90 - rotate)
end)

View File

@@ -0,0 +1,499 @@
--[[
| 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

View File

@@ -0,0 +1,189 @@
--[[
| 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/
--]]
-- All concommands for stormfox 2
local function SendMsg( ply, message )
message = "[SF2]: " .. message
if not ply then print( message ) return end
ply:PrintMessage(HUD_PRINTTALK, message)
end
-- Menu commands
if CLIENT then
-- Server menu
concommand.Add('stormfox2_svmenu', StormFox2.Menu.OpenSV, nil, "Opens SF serverside menu")
-- Client menu
concommand.Add('stormfox2_menu', StormFox2.Menu.Open, nil, "Opens SF clientside menu")
-- Controller
concommand.Add('stormfox2_controller', StormFox2.Menu.OpenController, nil, "Opens SF controller menu")
else -- Console only
concommand.Add("stormfox2_settings_reset", function( ply, cmd, args, argStr )
if ply and IsValid(ply) and not ply:IsListenServerHost() then return end -- Nope, console only
StormFox2.Setting.Reset()
end)
end
-- Weather
concommand.Add("stormfox2_setweather", function(ply, _, arg, _)
if CLIENT then return end
-- Check if valid weather
if #arg < 1 then
SendMsg(ply, "Weather can't be nil")
return
end
local s = string.upper(string.sub(arg[1],0,1)) .. string.lower(string.sub(arg[1], 2))
if not StormFox2.Weather.Get(s) then
SendMsg(ply, "Invalid weather [" .. s .. "]")
return
end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Weather.Set( s, tonumber( arg[2] or "1" ) or 1)
end)
end)
concommand.Add("stormfox2_setthunder", function(ply, _, _, argS)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
local n = tonumber(argS) or (StormFox2.Thunder.IsThundering() and 6 or 0)
StormFox2.Thunder.SetEnabled( n > 0, n )
end)
end)
-- Time and Date
concommand.Add("stormfox2_settime", function(ply, _, _, argS)
if CLIENT then return end
-- Check if valid
if not argS or string.len(argS) < 1 then
SendMsg(ply, "You need to type an input! Use formats like 'stormfox2_settime 19:00' or 'stormfox2_settime 7:00 PM'")
return
end
local tN = StormFox2.Time.StringToTime(argS)
if not tN then
SendMsg(ply, "Invalid input! Use formats like '19:00' or '7:00 PM'")
return
end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Time.Set( argS )
end)
end)
concommand.Add("stormfox2_setyearday", function(ply, _, _, argStr)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Date.SetYearDay( tonumber(argStr) or 0 )
end)
end)
concommand.Add("stormfox2_setwind", function(ply, _, _, argStr)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Wind.SetForce( tonumber(argStr) or 0 )
end)
end)
concommand.Add("stormfox2_setwindangle", function(ply, _, _, argStr)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Wind.SetYaw( tonumber(argStr) or 0 )
end)
end)
concommand.Add("stormfox2_settemperature", function(ply, _, _, argStr)
if CLIENT then return end
local temp = tonumber( string.match(argStr, "-?[%d]+") or "0" ) or 0
if string.match(argStr, "[fF]") then
temp = StormFox2.Temperature.Convert("fahrenheit","celsius",temp) or temp
end
StormFox2.Permission.EditAccess(ply,"StormFox WeatherEdit", function()
StormFox2.Temperature.Set( temp )
end)
end)
local function SetSetting( arg, arg2, ply )
if not arg or arg == "" then
SendMsg( ply, "You need to indecate a setting: stormfox2_setting [Setting] [Value]")
return
end
local obj = StormFox2.Setting.GetObject(arg)
if not obj then
SendMsg( ply, "Invalid setting: \"" .. tostring( arg ) .. "\"!")
return
end
if not arg2 then
SendMsg( ply, "You need a value for the setting!")
return
end
obj:SetValue( arg2 )
SendMsg( ply, tostring( arg ) .. " = " .. tostring( arg2 ))
end
local function AutoComplete(cmd, args)
args = string.TrimLeft(args)
local a = string.Explode(" ", args or "")
if #a < 2 then
local options = {}
for _, sName in pairs( StormFox2.Setting.GetAllServer() ) do
if string.find(string.lower(sName),string.lower(a[1]), nil, true) then
table.insert(options, "stormfox2_setting " .. sName)
end
end
if #options < 1 then
return {"stormfox2_setting [No Setting Found!]"}
elseif #options < 2 and "stormfox2_setting " .. args == options[1] then
local obj = StormFox2.Setting.GetObject(a[1])
if not obj then
return {"stormfox2_setting [Invalid Setting!]"}
end
return {"stormfox2_setting " .. a[1] .. " [" .. obj.type .. "]"}
end
return options
elseif not a[1] or string.TrimLeft(a[1]) == "" then
return {"stormfox2_setting [Setting] [Value]"}
else
local obj = StormFox2.Setting.GetObject(a[1])
if not obj then
return {"stormfox2_setting [Invalid Setting!]"}
else
return {"stormfox2_setting " .. a[1] .. " [" .. obj.type .. "]"}
end
end
end
concommand.Add("stormfox2_setting", function(ply, _, _, argStr)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox Settings", function()
local a = string.Explode(" ", argStr, false)
SetSetting(a[1],a[2])
end)
end, AutoComplete)
-- Forces the settings to save.
concommand.Add("stormfox2_settings_save", function(ply, _, _, _)
if CLIENT then return end
StormFox2.Permission.EditAccess(ply,"StormFox Settings", function()
SendMsg( ply, "Force saved settings to data/" .. StormFox2.Setting.GetSaveFile())
StormFox2.Setting.ForceSave()
end)
end)
-- Debug commands
if true then return end
concommand.Add("stormfox2_debug_spawnice", function(ply)
if ply and not ply:IsListenServerHost() then return end
SpawnIce()
end, nil, nil)
concommand.Add("stormfox2_debug_removeice", function(ply)
if ply and not ply:IsListenServerHost() then return end
RemoveIce()
end, nil, nil)

View File

@@ -0,0 +1,685 @@
--[[
| 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/
--]]
-- Weather functions
StormFox2.Menu = StormFox2.Menu or {}
local SF_SETWEATHER = 0
local SF_SETTEMP = 1
local SF_SETWIND_A = 2
local SF_SETWIND_F = 3
local SF_SETTIME = 4
local SF_SETTIME_S = 5
local SF_THUNDER = 6
local SF_YEARDAY = 7
if SERVER then
-- Gets called from sh_permission.lua
---Internally used by permissions to relay settings.
---@param ply Player
---@param uID number
---@param var any
---@deprecated
---@server
function StormFox2.Menu.SetWeatherData(ply, uID, var)
if uID == SF_SETWEATHER and type(var) == "table" then
if type(var[1]) ~= "string" or type(var[2])~= "number" then return end
StormFox2.Weather.Set( var[1], var[2] )
elseif uID == SF_SETTEMP and type(var) == "number" then
StormFox2.Temperature.Set( var )
elseif uID == SF_SETWIND_F and type(var) == "number" then
StormFox2.Wind.SetForce( var, 3 )
elseif uID == SF_SETWIND_A and type(var) == "number" then
StormFox2.Wind.SetYaw( var, 3 )
elseif uID == SF_SETTIME and type(var) == "number" then
StormFox2.Time.Set( var )
elseif uID == SF_SETTIME_S and type(var) == "number" then
if not StormFox2.Time.IsPaused() then
StormFox2.Time.Pause()
else
StormFox2.Time.Resume()
end
elseif uID == SF_THUNDER and type(var) == "boolean" then
StormFox2.Thunder.SetEnabled(var, 6)
elseif uID == SF_YEARDAY and type(var) == "number" then
StormFox2.Date.SetYearDay( var )
end
end
return
end
-- Send a request to change the weather
local function SetWeather( uID, var )
net.Start( StormFox2.Net.Permission )
net.WriteUInt(1, 1) -- SF_SERVEREDIT
net.WriteUInt(uID, 4)
net.WriteType(var)
net.SendToServer()
end
-- Menu
local t_col = Color(67,73,83)
local h_col = Color(84,90,103)
local b_col = Color(51,56,62)
local n = 0.7
local p_col = Color(51 * n,56 * n,62 * n)
local rad,cos,sin = math.rad, math.cos, math.sin
local grad = Material("gui/gradient_down")
local function DrawButton(self,w,h)
local hov = self:IsHovered()
local down = self:IsDown() or self._DEPRESSED
surface.SetDrawColor(b_col)
surface.DrawRect(0,0,w,h)
if self._DISABLED then
elseif down then
surface.SetDrawColor(p_col)
elseif hov then
surface.SetDrawColor(h_col)
else
surface.SetDrawColor(t_col)
end
surface.SetMaterial(grad)
surface.DrawTexturedRect(0,0,w,h)
surface.SetDrawColor(p_col)
surface.DrawOutlinedRect(0,0,w,h)
end
local bg_color = Color(27,27,27)
local side_color = Color(44,48,54)
local function OpenMenu( self )
local menu = vgui.Create("DNumberWang")
menu.m_numMin = nil
function menu:SetDraggable() end
local sx = 50 - self:GetWide()
local sy = 24 - self:GetTall()
menu:MakePopup()
menu:SetDraggable(true)
local x, y = self:LocalToScreen(-sx / 2,-sy / 2)
menu:SetPos( x,y )
menu:RequestFocus()
menu:SetSize(50,24)
menu.m_bIsMenuComponent = true
RegisterDermaMenuForClose( menu )
function menu:GetDeleteSelf() return true end
menu:SetValue( self:GetVal() )
menu.b = self
function menu:OnEnter( str )
CloseDermaMenus()
if not str then return end
self.b.p:OnMenu( tonumber( str ) )
end
end
local color_gray = Color(155,155,155)
local function SliderNumber(self)
local p = vgui.Create("DPanel", self)
p:SetTall(18)
p._ta = 30
function p:Paint() end
function p:SetVal(n) self.val = n end
function p:GetVal() return self.val or 0 end
p._aimval = nil
AccessorFunc(p, "_min", "Min", FORCE_NUMBER)
AccessorFunc(p, "_max", "Max", FORCE_NUMBER)
p:SetMax(1)
p:SetMin(0)
function p:GetAP()
return (self._aimval - self:GetMin() ) / ( self:GetMax() - self:GetMin() )
end
function p:GetP()
return (self:GetVal() - self:GetMin() ) / ( self:GetMax() - self:GetMin() )
end
function p:SetP(f)
p:SetVal( -f * self:GetMin() + f * self:GetMax() + self:GetMin() )
end
local slider = vgui.Create("DButton", p)
local button = vgui.Create("DButton", p)
button:SetText("")
button.p = p
slider:SetText("")
function button:SetVal( n ) p:SetVal(n) end
function button:GetVal() return p:GetVal() end
function button:DoClick()
OpenMenu(self)
end
function p:OnMenu( val )
if not val then return end
self:SetVal( val )
self:OnVal( val )
end
function p:DrawText( num ) return num end
function button:Paint(w,h)
if not self:IsEnabled() then return end
surface.SetDrawColor(0, 0, 0, 155)
surface.DrawRect(0, 0, w, h)
local s = p:DrawText( p:GetVal() )
draw.DrawText(s, "DermaDefault", w / 2, 2, color_white, TEXT_ALIGN_CENTER)
end
function slider:Paint(w,h)
local v = math.Clamp(p:GetP(), 0, 1)
local a = p._aimval and math.Clamp(p:GetAP(), 0, 1)
local pos = w * v
-- Background
draw.RoundedBox(30, 0, h / 2 - 3, w, 4, color_black)
-- White
draw.RoundedBox(30, 0, h / 2 - 3, pos, 4, color_white)
if a and v ~= a then
local pos2= w * a
local mi = math.min(pos, pos2)
draw.RoundedBox(30, mi, h / 2 - 3, math.abs(pos - pos2),4, color_gray)
draw.RoundedBox(30, pos2 - 1, 0, 3, h, color_gray)
end
draw.RoundedBox(30, pos - 1, 0, 3, h, color_white)
end
function p:PerformLayout(w, h)
button:SetPos(w - self._ta,0)
button:SetSize(self._ta, h)
if self._ta > 0 then
slider:SetSize(w - self._ta - 5,18)
else
slider:SetSize(w,18)
end
slider:SetPos(0, h / 2 - 9)
end
function slider:OnDepressed()
self._update = true
end
function slider:OnReleased()
self._update = false
local x,y = self:LocalCursorPos()
local f = math.Round(math.Clamp(x / self:GetWide(), 0, 1), 2)
p:SetP( f )
p:OnVal( p:GetVal() )
end
function slider:Think()
if p.Think2 then
p:Think2()
end
if not self._update then return end
local x,y = self:LocalCursorPos()
local f = math.Round(math.Clamp(x / self:GetWide(), 0, 1), 2)
p:SetP( f )
end
function p:SetTextSize( num)
self._ta = num
if num <= 0 then
button:SetEnabled(false)
else
button:SetEnabled(true)
end
self:InvalidateLayout()
end
function p:OnVal( val ) end
p:SetVal(0.6)
return p
end
local bottom_size = 24
local col_ba = Color(0,0,0,155)
local col_dis = Color(125,125,125,125)
local m_cir = Material("stormfox2/hud/hudring2.png")
local m_thunder = Material("stormfox2/hud/w_cloudy_thunder.png")
local padding = 15
local padding_y = 5
local function addW( w_select, v, p )
local b = vgui.Create("DButton",w_select)
b:SetSize(32,32)
b:SetText("")
b:DockMargin(0,0,0,0)
w_select:AddPanel(b)
b.weather = v
b:SetToolTip(v)
function b:OnCursorEntered()
local w = StormFox2.Weather.Get(self.weather)
if not IsValid(w) then return end -- Something bad happen
b:SetToolTip(w:GetName(StormFox2.Time.Get(), StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), p:GetVal() / 100))
end
function b:Paint(w,h)
DrawButton(self,w,h)
local weather = StormFox2.Weather.Get(b.weather)
local mat = weather.GetSymbol and weather.GetSymbol(_,StormFox2.Temperature.Get())
if mat then
surface.SetDrawColor(255,255,255)
surface.SetMaterial(mat)
surface.DrawTexturedRect(5,5,w - 10,h - 10)
end
end
function b:DoClick()
SetWeather(SF_SETWEATHER, {self.weather, p:GetVal() / 100})
end
end
local function versionGet()
if not StormFox2.Version then return "?" end
return string.format("%.2f", StormFox2.Version)
end
local function Init(self)
self:SetSize(180,432)
self:SetPos(math.min(ScrW() * 0.8, ScrW() - 180), ScrH() / 2 - 200)
self:SetTitle("")
self.btnMaxim:SetVisible( false )
self.btnMinim:SetVisible( false )
function self:Paint(w,h)
surface.SetDrawColor(side_color)
surface.DrawRect(0,0,w,h)
-- Top
local t = "StormFox " .. versionGet()
surface.SetDrawColor(p_col)
surface.DrawRect(0,0,w,24)
surface.SetFont("SF2.W_Button")
local tw,th = surface.GetTextSize(t)
surface.SetTextColor(color_white)
surface.SetTextPos(10,th / 2 - 2)
surface.DrawText(t)
end
self:DockMargin(0,24,0,0)
self:DockPadding(0,24,0,0)
-- Weather
local m_weather = vgui.Create("DPanel", self)
m_weather:SetTall(70)
m_weather:Dock(TOP)
m_weather.Paint = function() end
self.weather = m_weather
local w_button = vgui.Create("DLabel", m_weather)
w_button:SetText("")
w_button:SetTall(28)
function w_button:Paint(w,h)
local t = "Set Weather"
surface.SetTextColor(color_white)
surface.SetFont("SF2.W_Button")
local tw,th = surface.GetTextSize(t)
surface.SetTextPos(w / 2 - tw / 2, h / 2 - th / 2)
surface.DrawText(t)
end
local w_select = vgui.Create("DHorizontalScroller", m_weather)
w_select:SetOverlap( -4 )
w_select.num = 0
-- Percent & W
local p = SliderNumber( self )
p:SetToolTip('#sf_weatherpercent')
p:SetTextSize(40)
if StormFox2.Weather.GetCurrent() == StormFox2.Weather.Get('Clear') then
p:SetVal(85)
else
p:SetVal(math.Round(math.Clamp(StormFox2.Weather.GetPercent() * 100, 0, 100), 2))
end
function p:OnVal(x)
SetWeather(SF_SETWEATHER, {StormFox2.Weather.GetCurrent().Name, x / 100})
end
function m_weather:PerformLayout(w, h)
w_button:SetWide(w * 0.7)
w_button:SetPos(w * 0.15,5)
-- Calc the wide
local wide = w_select.num * (32 - w_select.m_iOverlap)
-- If weathers won't fit, make it default size and pos
if wide >= w * 0.9 then
w_select:SetSize(w * 0.9,32)
w_select:SetPos(w * 0.05, 32)
else -- Calc calculate the middle
w_select:SetSize(wide,32)
w_select:SetPos(w * 0.05 + (w * 0.9 - wide) / 2 , 32)
end
end
local t = StormFox2.Weather.GetAll()
addW(w_select, "Clear", p) -- Always add clear
if table.HasValue(t, "Cloud") then
addW(w_select, "Cloud", p)
w_select.num = w_select.num + 1
end
if table.HasValue(t, "Rain") then
addW(w_select, "Rain", p)
w_select.num = w_select.num + 1
end
if table.HasValue(t, "Fog") then
addW(w_select, "Fog", p)
w_select.num = w_select.num + 1
end
for k,v in ipairs(t) do
if v == "Clear" or v == "Cloud" or v == "Rain" or v == "Fog" then continue end -- Ignore
addW(w_select, v, p)
w_select.num = w_select.num + 1
end
p:SetMin(1)
p:SetMax(100)
p:Dock(TOP)
p:DockMargin(padding,0,padding,padding_y)
function p:DrawText( s )
return s .. "%"
end
-- Thunder
local tP = vgui.Create("DPanel", self)
tP:Dock(TOP)
tP:SetTall(32)
tP.Paint = empty
local thunder = vgui.Create("DButton", tP)
thunder:NoClipping( true )
thunder:SetSize(33, 32)
thunder:SetText('')
function tP:PerformLayout(w, h)
thunder:SetPos(w / 2 - 16,0)
end
function thunder:Paint(w,h)
local cW = StormFox2.Weather.GetCurrent()
local hasThunder = cW.Name ~= "Clear"
self._DEPRESSED = StormFox2.Thunder.IsThundering()
self._DISABLED = not hasThunder and not self._DEPRESSED
DrawButton(self,w,h)
if not self._DISABLED then
surface.SetDrawColor(color_white)
else
surface.SetDrawColor(col_dis)
end
surface.SetMaterial(m_thunder)
surface.DrawTexturedRect(5,5,w - 10,h - 10)
end
function thunder:DoClick()
local cW = StormFox2.Weather.GetCurrent()
local hasThunder = cW.Name ~= "Clear"
local isth = StormFox2.Thunder.IsThundering()
if not isth and not hasThunder then
return
end
SetWeather(SF_THUNDER, not isth)
end
-- Temperature
local t = vgui.Create("DPanel", self)
t:SetTall(30)
t:Dock(TOP)
t:DockMargin(padding,padding_y,padding,0)
local text = language.GetPhrase("#temperature")
t.text = string.upper(text[1]) .. string.sub(text, 2)
function t:Paint(w,h)
surface.SetFont("SF2.W_Button")
local tw,th = surface.GetTextSize(self.text)
surface.SetTextColor(color_white)
surface.SetTextPos(w / 2 - tw / 2,th / 2 - 2)
surface.DrawText(self.text)
end
local tempslider = SliderNumber(self)
local function Conv( n ) return math.Round(StormFox2.Temperature.Convert(nil,StormFox2.Temperature.GetDisplayType(),n), 1) end
tempslider:DockMargin(padding,0,padding,padding_y)
tempslider:Dock(TOP)
tempslider:SetMin(Conv(-20))
tempslider:SetMax(Conv(40))
tempslider:SetTextSize(40)
function tempslider:OnVal( num )
num = math.Round(StormFox2.Temperature.Convert(StormFox2.Temperature.GetDisplayType(),nil,num), 1)
SetWeather(SF_SETTEMP, num)
end
function tempslider:DrawText( n )
return n .. StormFox2.Temperature.GetDisplaySymbol()
end
tempslider:SetVal( math.Round(StormFox2.Temperature.GetDisplay(),1) )
function tempslider:Think()
tempslider._aimval = math.Round(StormFox2.Temperature.GetDisplay(StormFox2.Data.GetFinal( "Temp", 20 )),1)
tempslider:SetVal( math.Round(StormFox2.Temperature.GetDisplay(),1) )
end
-- Wind Ang
local t = vgui.Create("DPanel", self)
t:DockMargin(padding,padding_y,padding,0)
t:SetTall(30)
t:Dock(TOP)
local text = language.GetPhrase("#sf_wind")
t.text = string.upper(text[1]) .. string.sub(text, 2)
function t:Paint(w,h)
surface.SetFont("SF2.W_Button")
local tw,th = surface.GetTextSize(self.text)
surface.SetTextColor(color_white)
surface.SetTextPos(w / 2 - tw / 2,th / 2 - 2)
surface.DrawText(self.text)
end
local b = vgui.Create("DPanel", self)
function b:Paint() end
b:SetSize(80,80)
b:Dock(TOP)
local w_ang = vgui.Create("DButton", b)
w_ang:SetToolTip('#sf_setang.desc')
w_ang:SetText("")
function b:PerformLayout(w, h)
w_ang:SetSize(h,h)
w_ang:SetPos(w / 2 - h / 2)
end
function w_ang:Paint( w, h )
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
surface.SetDrawColor(col_ba)
surface.SetMaterial(m_cir)
surface.DrawTexturedRect(0,0,w,h)
local windang = EyeAngles().y - (StormFox2.Wind.GetYaw() or 0)
local wind = StormFox2.Wind.GetForce() or 0
local t = {{x = w / 2,y = h / 2, u=0.5,v=0.5}}
local l = math.Clamp(wind,0,70) / 3
if l < 1 then
surface.SetDrawColor(155,255,155)
l = 2
else
surface.SetDrawColor(155,155,255)
end
local nn = 90 - l * 5
for i = 0,l - 1 do
local c,s = cos(rad(i * 10 + windang + nn)),sin(rad(i * 10 + windang + nn))
local x = c * w / 2 + w / 2
local y = s * h / 2 + h / 2
table.insert(t,{x = x,y = y, u = (c + 1) / 2, v = (s + 1) / 2})
end
local c,s = cos(rad(l * 10 + windang + nn)),sin(rad(l * 10 + windang + nn))
local x = c * w / 2 + w / 2
local y = s * h / 2 + h / 2
table.insert(t,{x = x,y = y, u=(c + 1) / 2,v = (s + 1) / 2})
--draw.NoTexture()
surface.DrawPoly(t)
surface.SetFont("DermaDefault")
local t = language.GetPhrase("#sf_setang")
local tw,th = surface.GetTextSize(t)
surface.SetTextPos(w / 2 - tw / 2, h / 2 - th / 2)
surface.DrawText(t)
render.PopFilterMag()
render.PopFilterMin()
end
function w_ang:DoClick()
SetWeather(SF_SETWIND_A, (EyeAngles().y + 180) % 360)
end
-- Wind
local p = vgui.Create("DPanel", self)
p:SetTall(22)
p:Dock(TOP)
p:DockMargin(padding,padding_y,padding,0)
function p:Paint(w,h)
local f = math.Round(StormFox2.Wind.GetForce() or 0, 1)
local bf,desc = StormFox2.Wind.GetBeaufort(f)
local text = f .."m/s : " .. language.GetPhrase(desc)
surface.SetFont("DermaDefault")
surface.SetTextColor(color_white)
local tw,th = surface.GetTextSize(text)
surface.SetTextPos(w / 2 - tw / 2, h / 2 - th / 2)
surface.DrawText(text)
end
local windslide = SliderNumber(self)
windslide:SetToolTip('#sf_setwind')
windslide:Dock(TOP)
windslide:DockMargin(padding,0,padding,0)
windslide:SetMin(0)
windslide:SetMax(70)
windslide:SetTextSize(0)
windslide:SetVal( StormFox2.Wind.GetForce() or 0 )
function windslide:OnVal( num )
SetWeather(SF_SETWIND_F, num)
end
function windslide:Think2()
windslide._aimval = StormFox2.Data.GetFinal( "Wind", 0 )
windslide:SetVal( StormFox2.Wind.GetForce() or 0 )
end
-- Time
local p = vgui.Create("DPanel", self)
p:SetTall(40)
p.Paint = function() end
local t = vgui.Create("SF_TIME", p)
function t:Think()
self:SetValue( StormFox2.Time.Get() )
end
function t:OnNewValue( var )
SetWeather( SF_SETTIME, var )
end
p:Dock(TOP)
local pause = vgui.Create("DButton", p)
pause.state = 1
pause:SetSize(30, 30)
function p:PerformLayout(w, h)
pause:SetPos(10,10)
t:SetPos( 42 ,10)
t:SetWide( w - 20 - 27 )
end
local a = StormFox2.Setting.GetObject("day_length")
local b = StormFox2.Setting.GetObject("night_length")
local r = Material("gui/point.png")
local z = Material("gui/workshop_rocket.png")
function pause:Think()
if StormFox2.Time.IsPaused() then
self.state = 0 -- pause
else
self.state = 1 -- running
end
end
pause:SetText("")
--pause.Paint = DrawButton
--t.bg.Paint = DrawButton
--
--t.ampm.Paint = DrawButton
--function t.ampm:UpdateColours()
-- self:SetTextStyleColor( color_white )
--end
--t.hour.color = color_white
--t.min.color = color_white
local c = Color(0,0,0,225)
function pause:PaintOver(w,h)
local s = 15
if self.state == 0 then
surface.SetMaterial(r)
surface.SetDrawColor(c)
surface.DrawTexturedRectRotated(w / 2 + 2,h / 2,w - s,h - s, 90)
else
surface.SetMaterial(z)
surface.SetDrawColor(c)
surface.DrawTexturedRectRotated(w / 2 - 5,h / 2,w - s * 1.1,h, 0)
surface.DrawTexturedRectRotated(w / 2 + 5,h / 2,w - s * 1.1,h, 0)
end
end
function pause:DoClick()
SetWeather(SF_SETTIME_S, 0)
end
pause:SetPos(20 ,10)
end
-- Caht status
local openChat = false
hook.Add("StartChat","StormFox2.Controller.Disable",function()
openChat = true
end)
hook.Add("FinishChat","StormFox2.Controller.Enable",function()
openChat = false
end)
local mat = Material("gui/workshop_rocket.png")
local c = Color(55,55,55)
---Builds the controller
---@deprecated
---@return userdata panel
---@client
function StormFox2.Menu._OpenController()
if _SF_CONTROLLER then
_SF_CONTROLLER:Remove()
end
if spawnmenu and spawnmenu.SetActiveControlPanel then
spawnmenu.SetActiveControlPanel(nil)
end
local p = vgui.Create("DFrame")
if not p then return end
_SF_CONTROLLER = p
Init(p)
local settings = vgui.Create("DButton", p)
settings:SetSize(31, 24)
settings:SetPos(p:GetWide() - 31 * 2 - 4)
settings:SetIcon('icon16/cog_edit.png')
settings:SetText("")
settings:SetToolTip("#spawnmenu.utilities.server_settings")
function settings:DoClick()
surface.PlaySound("buttons/button14.wav")
RunConsoleCommand("stormfox2_svmenu")
end
function settings:Paint() end
function p:PaintOver(w,h)
if self.enabled then return end
local x,y = 0, h / 2
surface.SetMaterial(mat)
surface.SetDrawColor(HSLToColor(240, 0.3,0.5 + sin(CurTime() * 1.5) / 10))
surface.DrawTexturedRectUV(0,h * 0.4,w,h * 0.2,0.2,-0.2,0.8,1)
draw.DrawText("#sf_holdc", "SF2.W_Button", w / 2, h / 2, color_white, TEXT_ALIGN_CENTER)
end
function p:Think()
local x,y = self:LocalCursorPos(0,0)
local inside = x > 0 and x < self:GetWide() and y > 0 and y < self:GetTall()
if not self.enabled and input.IsKeyDown(KEY_C) and not openChat and not gui.IsConsoleVisible() then
self.enabled = true
self.btnClose:SetDisabled( false )
self:MakePopup()
self:SetSelected()
elseif self.enabled then
if input.IsKeyDown(KEY_C) then return end -- If KEY is down, don't disable
if self:HasHierarchicalFocus() and not self:HasFocus() then return end -- Typing in something. Don't disable.
if inside then return end -- Mouse is inside controller. Don't disable yet.
self.enabled = false
self.btnClose:SetDisabled( true )
self:SetMouseInputEnabled(false)
self:SetKeyboardInputEnabled(false)
end
end
return _SF_CONTROLLER
end
---Opens the controller
---@client
function StormFox2.Menu.OpenController()
net.Start("StormFox2.menu")
net.WriteBool(false)
net.SendToServer()
end
---Closes the controller
---@client
function StormFox2.Menu.CloseController()
if _SF_CONTROLLER then
_SF_CONTROLLER:Remove()
end
end
-- Controller
list.Set( "DesktopWindows", "StormFoxController", {
title = "#sf_wcontoller",
icon = "stormfox2/hud/controller.png",
width = 960,
height = 700,
onewindow = true,
init = function( icon, window )
window:Remove()
surface.PlaySound("buttons/button14.wav")
StormFox2.Menu.OpenController()
end
} )
concommand.Add('stormfox2_controller', StormFox2.Menu.OpenController, nil, "Opens SF controller menu")

View File

@@ -0,0 +1,288 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
Use the map-data to set a minimum and maximum fogdistance
---------------------------------------------------------------------------]]
StormFox2.Setting.AddSV("enable_svfog",true,nil, "Effects")
if CLIENT then StormFox2.Setting.AddCL("enable_fog",true, "sf_enable_fog") end
StormFox2.Setting.AddSV("enable_fogz",false,nil, "Effects")
StormFox2.Setting.AddSV("overwrite_fogdistance",-1,nil, "Effects", -1, 800000)
StormFox2.Setting.SetType("overwrite_fogdistance","special_float")
StormFox2.Setting.AddSV("allow_fog_change",engine.ActiveGamemode() == "sandbox",nil, "Effects")
StormFox2.Fog = {}
-- Local functions
local function fogEnabledCheck()
if not StormFox2.Setting.SFEnabled() then return false end
if not StormFox2.Setting.GetCache("enable_svfog", true) then return false end
if not StormFox2.Setting.GetCache("allow_fog_change", false) then return true end
return StormFox2.Setting.GetCache("enable_fog", true)
end
local _fS, _fE, _fD = 0,400000,1
local function fogStart( f )
_fS = f
end
local function fogEnd( f )
_fE = f
end
local function fogDensity( f )
_fD = f
end
local function getFogFill()
if _fS >= 0 then return 0 end
return -_fS / (_fE - _fS) * _fD * 0.1
end
-- Makes it so fog isn't linear
local e = 2.71828
local function fogCalc(b, a, p)
if a == b then return a end
p = e^(-8.40871*p)
local d = b - a
return a + d * p
end
---Returns the start of the fog.
---@return number
---@shared
function StormFox2.Fog.GetStart()
return math.max(0, _fS)
end
---Returns the end of fog.
---@return number
---@shared
function StormFox2.Fog.GetEnd()
return _fE
end
-- Locate / Calculate the default fog-data
local map_distance, map_farZ = -1, -1
local tab = StormFox2.Map.FindClass("env_fog_controller")
if #tab < 1 then
map_distance = 400000
else
-- Set a minimum
map_distance = 6000
for _, data in ipairs(tab) do
map_farZ = math.max(map_farZ, data.farz)
-- Calculate fog-brightness. We can use this to scale the map-distance up to match the fog.
local col_brightness = 1
local density = (tonumber( data.fogmaxdensity or "" ) or 1)
if data.fogcolor then
local fcol = string.Explode(" ", data.fogcolor)
col_brightness = (0.2126 * fcol[1] + 0.7152 * fcol[2] + 0.0722 * fcol[3]) / 255
end
density = density * col_brightness
map_distance = math.max(((data.fogend or 6000) / density), map_distance)
end
-- It is important we don't overshoot farZ
if map_farZ > 0 then
map_distance = math.min(map_distance, map_farZ)
end
end
---Returns the fog-amount. 0 - 1
---@return number
---@shared
function StormFox2.Fog.GetAmount()
return 1 - _fE / map_distance
end
---Returns the fog-distance ( Same as StormFox2.Fog.GetEnd(), but uses the map as a fallback )
---@return number
---@shared
function StormFox2.Fog.GetDistance()
return _fE or map_distance
end
-- Returns the default fog-distance for clear weather.
local function getDefaultDistance()
local ov = StormFox2.Setting.GetCache("overwrite_fogdistance",-1)
if ov > -1 then
return ov
end
return map_distance
end
-- Returns the fog-distance.
local function getAimDistance(bFinal)
local cW = StormFox2.Weather.GetCurrent()
local ov = getDefaultDistance()
if cW.Name == "Clear" then return ov end
local perc = bFinal and StormFox2.Weather.GetFinishPercent() or StormFox2.Weather.GetPercent()
local a = math.min(cW:Get('fogDistance'), ov)
if a == ov then return ov end -- This weathertype doesn't change the fog .. or is higer than default
if not a or perc <= 0 then return ov end -- If weather percent is 0 or under. Return the "clear" distance.
if perc >= 1 then return a end -- If weather is higer or equal to 1, return the base value.
return fogCalc(ov, a, perc)
end
if SERVER then
local loaded, data, f_FogZ = true
---Sets the fogZ distance. Seems buggy atm, use at own rist.
---@param num number
---@param nTimer number
---@server
function StormFox2.Fog.SetZ(num, nTimer)
timer.Remove( "sf_fog_timer" )
if nTimer then
timer.Create("sf_fog_timer", nTimer, 1, function()
StormFox2.Fog.SetZ(num)
end)
return
end
f_FogZ = num
if not loaded then
data = num
return
end
if not num then num = map_farZ end
for k,v in ipairs( StormFox2.Ent.env_fog_controllers or {} ) do
if not IsValid(v) then continue end
v:SetKeyValue("farz", num)
end
end
---Returns the fogz distance.
---@return number
---@server
function StormFox2.Fog.GetZ()
if not StormFox2.Setting.Get("enable_fogz", false) then return map_farZ end
return f_FogZ or (StormFox2.Fog.GetDistance() + 100)
end
hook.Add("StormFox2.PostEntityScan", "StormFox2.Fog.Initz", function()
loaded = true
if data then
StormFox2.Fog.SetZ(data)
data = nil
end
end)
hook.Add("StormFox2.weather.postchange", "StormFox2.Fog.Updater", function( sName ,nPercentage, nDelta )
local old_fE = _fE or map_distance
_fE = getAimDistance(true)
if _fE > 3000 then
_fS = 0
else
_fS = _fE - 3000
end
-- Check fogZ distance
if not StormFox2.Setting.Get("enable_fogz", false) then return end
if old_fE > _fE then -- The fog shriks
StormFox2.Fog.SetZ(_fE * 2 + 100, nDelta)
elseif old_fE < _fE then -- The fog grows
StormFox2.Fog.SetZ(_fE * 2 + 100)
end
end)
timer.Create("StormFox2.Fog.SVUpdate", 2, 0, function()
local cWD = StormFox2.Weather.GetCurrent().Dynamic or {}
if cWD.fogDistance then return end
_fE = getAimDistance(true)
end)
---Returns the fog-color.
---@return Color
---@server
function StormFox2.Fog.GetColor()
return StormFox2.Mixer.Get("fogColor", StormFox2.Mixer.Get("bottomColor",color_white) ) or color_white
end
return
end
----- Fog render and clientside -----
-- Fog logic and default render
-- Returns the "distance" to outside
local f_outside = 0
local f_indoor = -1
local f_lastDist = map_distance
local function outSideVar()
local env = StormFox2.Environment.Get()
if env.outside then
return f_outside
end
if not env.nearest_outside then
return f_indoor
end
local dis = StormFox2.util.RenderPos():Distance(env.nearest_outside) / 300
if dis > 1 then
return f_indoor
end
return dis
end
hook.Add("Think", "StormFox2.Fog.Updater", function()
-- Figure out the fogdistance we should have
local f_envfar = outSideVar()
local fog_dis = getAimDistance()
local fog_indoor = StormFox2.Mixer.Get("fogIndoorDistance",3000)
if f_envfar == f_indoor then -- Indoors
fog_dis = math.max(fog_dis, fog_indoor)
elseif f_envfar ~= f_outside then
fog_dis = Lerp(f_envfar + 0.1, fog_dis, fog_indoor)
end
_fE = math.Approach(_fE, fog_dis, math.max(10, _fE) * FrameTime())
if _fE > 3000 then
_fS = 0
else
_fS = _fE - 3000
end
end)
local f_Col = color_white
local SkyFog = function(scale)
if _fD <= 0 then return end
if not scale then scale = 1 end
if not fogEnabledCheck() then return end
f_Col = StormFox2.Mixer.Get("fogColor", StormFox2.Mixer.Get("bottomColor") ) or color_white
-- Apply fog
local tD = StormFox2.Thunder.GetLight() / 2055
render.FogMode( 1 )
render.FogStart( StormFox2.Fog.GetStart() * scale )
render.FogEnd( StormFox2.Fog.GetEnd() * scale )
render.FogMaxDensity( (_fD - tD) * 0.999 )
render.FogColor( f_Col.r,f_Col.g,f_Col.b )
return true
end
hook.Add("SetupSkyboxFog","StormFox2.Sky.Fog",SkyFog)
hook.Add("SetupWorldFog","StormFox2.Sky.WorldFog",SkyFog)
-- Returns the fog-color.
function StormFox2.Fog.GetColor()
return f_Col or color_white
end
-- Additional Fog render
local m_fog = Material('stormfox2/effects/fog_sphere')
local l_fogz = 0
hook.Add("StormFox2.2DSkybox.FogLayer", "StormFox2.Fog.RSky", function( viewPos )
if not fogEnabledCheck() then return end
local v = Vector(math.cos( viewPos.y ), math.sin( viewPos.y ), 0)
m_fog:SetVector("$color", Vector(f_Col.r,f_Col.g,f_Col.b) / 255)
m_fog:SetFloat("$alpha", math.Clamp(5000 / _fE, 0, 1))
render.SetMaterial( m_fog )
local tH = math.min(StormFox2.Environment.GetZHeight(), 2100)
if tH ~= l_fogz then
local delta = math.abs(l_fogz, tH) / 2
l_fogz = math.Approach( l_fogz, tH, math.max(delta, 10) * 5 * FrameTime() )
end
local h = 2000 + 6000 * StormFox2.Fog.GetAmount()
render.DrawSphere( Vector(0,0,h - l_fogz * 4) , -30000, 30, 30, color_white)
end)
local mat = Material("color")
local v1 = Vector(1,1,1)
hook.Add("PostDrawOpaqueRenderables", "StormFox2.Sky.FogPDE", function()
if _fS >= 0 or _fD <= 0 then return end
if not fogEnabledCheck() then return end
local a = getFogFill()
mat:SetVector("$color",Vector(f_Col.r / 255,f_Col.g / 255,f_Col.b / 255))
mat:SetFloat("$alpha",a)
render.SetMaterial(mat)
render.DrawScreenQuad()
mat:SetVector("$color",v1)
mat:SetFloat("$alpha",1)
end)

View File

@@ -0,0 +1,298 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
Footsteps and logic.
- Overrides default footstepsounds with terrain-sounds
---------------------------------------------------------------------------]]
local NetL = {"npc_zombie", "npc_poisonzombie", "npc_vortigaunt", "npc_antlion", "npc_fastzombie"} -- These entites only play sounds serverside and needs to be networked.
local BL = {"npc_hunter"} -- Tehse entities should not get their sound replaced
local find = string.find
local bAlwaysFootstep = false -- This is set to true on cold maps
local defaultSnowName = "snow.step"
local defaultSnowSnd = {
"stormfox/footstep/footstep_snow0.ogg",
"stormfox/footstep/footstep_snow1.ogg",
"stormfox/footstep/footstep_snow2.ogg",
"stormfox/footstep/footstep_snow3.ogg",
"stormfox/footstep/footstep_snow4.ogg",
"stormfox/footstep/footstep_snow5.ogg",
"stormfox/footstep/footstep_snow6.ogg",
"stormfox/footstep/footstep_snow7.ogg",
"stormfox/footstep/footstep_snow8.ogg",
"stormfox/footstep/footstep_snow9.ogg"
}
if SERVER then
util.AddNetworkString("StormFox2.feetfix")
end
-- We use this to cache the last foot for the players.
local lastFoot = {}
hook.Add("PlayerFootstep", "StormFox2.lastfootprint", function(ply, pos, foot, sound, volume, filter, ...)
lastFoot[ply] = foot
end)
-- Local functions
--local noSpam = {}
local cache = {}
-- Returns the foot from sounddata
local function GetFootstep(tab)
local ent = tab.Entity
if not ent or not IsValid(ent) then return end
if not ent:IsPlayer() and not ent:IsNPC() and not ent:IsNextBot() then return end
--if (noSpam[ent] or 0) > CurTime() then return end
-- Check to see if it is a footstep
local OriginalSnd = tab.OriginalSoundName:lower()
local foot = -1
if cache[OriginalSnd] then
foot = cache[OriginalSnd]
elseif string.match(OriginalSnd, "npc_antlionguard.farstep") or string.match(OriginalSnd, "npc_antlionguard.nearstep") then
foot = lastFoot[ent] or -1
elseif find(OriginalSnd, "stepleft",1,true) or find(OriginalSnd, "gallopleft",1,true) then
foot = 0
cache[OriginalSnd] = 0
elseif find(OriginalSnd, "stepright",1,true) or find(OriginalSnd, "gallopright",1,true) then
foot = 1
cache[OriginalSnd] = 1
elseif find(OriginalSnd, ".footstep",1,true) or find(tab.SoundName:lower(),"^player/footsteps",1) then
foot = lastFoot[ent] or -1
else -- Invalid
return
end
-- No footstep spam
--noSpam[ent] = CurTime() + 0.01
return foot
end
-- TraceHull for the given entity
local function EntTraceTexture(ent,pos) -- Returns the texture the entity is "on"
local mt = ent:GetMoveType()
if mt < 2 or mt > 3 then return end -- Not walking.
local filter = ent
if ent.GetViewEntity then
filter = ent:GetViewEntity()
end
local t = util.TraceHull( {
start = pos + Vector(0,0,30),
endpos = pos + Vector(0,0,-60),
maxs = ent:OBBMaxs(),
mins = ent:OBBMins(),
collisiongroup = ent:GetCollisionGroup(),
filter = filter
} )
if not t.Hit then return end -- flying
if t.Entity and IsValid(t.Entity) and t.HitNonWorld and t.HitTexture == "**studio**" then
return
end
return t.HitTexture
end
-- Returns true if the entity is on replaced texture.
local function IsOnReplacedTex(ent,snd,pos)
if ent._sf2ns and ent._sf2ns > CurTime() then return false, ent._sf2nt or "nil" end
ent._sf2ns = CurTime() + 0.1
local sTexture = EntTraceTexture(ent,pos)
ent._sf2nt = sTexture
if not sTexture then return false,"nil" end
local mat = Material(sTexture)
if not mat then return false, sTexture end
if mat:IsError() and (ent:IsNPC() or string.find(snd,"grass") or string.find(snd,"dirt")) then -- Used by maps
return true, sTexture
end
if StormFox2.Terrain.HasMaterialChanged(mat) then return true, sTexture end
return false,sTexture
end
-- Footstep overwrite and logic
hook.Add("EntityEmitSound", "StormFox2.footstep.detecter", function(data)
if not StormFox2.Terrain then return end
local cT = StormFox2.Terrain.GetCurrent()
if not cT then return end
-- Only enable if we edit or need footsteps.
if not (bAlwaysFootstep or (cT and cT.footstepLisen)) then return end
-- Check if the server has disabled the footprint logic on their side.
if SERVER and not game.SinglePlayer() and not StormFox2.Setting.GetCache("footprint_enablelogic",true) then return end
-- Check if it is a footstep sound of some sort.
local foot = GetFootstep(data) -- Returns [-1 = invalid, 0 = left, 1 = right]
if not foot then return end
-- Checks to see if the texturem the entity stands on, have been replaced.
local bReplace, sTex = IsOnReplacedTex(data.Entity,data.SoundName:lower(),data.Pos or data.Entity:GetPos())
-- Overwrite the sound if needed.
local changed
if bReplace and cT.footprintSnds then
if cT.footprintSnds[2] then
data.OriginalSoundName = cT.footprintSnds[2] .. (foot == 0 and "left" or "right")
end
if not cT.footprintSnds[1] then
data.SoundName = "ambient/_period.wav"
else
data.SoundName = table.Random(cT.footprintSnds[1])
data.OriginalSoundName = data.SoundName
end
changed = true
end
-- Call footstep hook
hook.Run("StormFox2.terrain.footstep", data.Entity, foot, data.SoundName, sTex, bReplace )
-- Singleplayer and server-sounds fix
if SERVER and (game.SinglePlayer() or table.HasValue(NetL, data.Entity:GetClass())) then
net.Start("StormFox2.feetfix",true)
net.WriteEntity(data.Entity)
net.WriteInt(foot or 1,2)
net.WriteString(data.SoundName)
net.WriteString(sTex)
net.WriteBool(bReplace)
net.Broadcast()
end
-- Call terrain function
if cT.footstepFunc then
cT.footstepFunc(data.Entity, foot, data.SoundName, sTex, bReplace)
end
return changed
end)
-- Singleplayer and entity fix
if CLIENT then
net.Receive("StormFox2.feetfix",function()
local cT = StormFox2.Terrain.GetCurrent()
if not cT then return end
local ent = net.ReadEntity()
if not IsValid(ent) then return end
local foot = net.ReadInt(2)
local sndName = net.ReadString()
local sTex = net.ReadString()
local bReplace = net.ReadBool()
if cT.footstepFunc then
cT.footstepFunc(ent, foot, sndName, sTex, bReplace)
end
hook.Run("StormFox2.terrain.footstep", ent, foot, sndName, sTex, bReplace)
end)
end
--[[-------------------------------------------------------------------------
Footprint render
---------------------------------------------------------------------------]]
if CLIENT then
local sin,cos,rad,clamp,ceil,min = math.sin,math.cos,math.rad,math.Clamp,math.ceil,math.min
local prints = {}
local footstep_maxlife = 30
local function ET(pos,pos2,mask,filter)
local t = util.TraceLine( {
start = pos,
endpos = pos + pos2,
mask = mask,
filter = filter
} )
if not t then -- tracer failed, this should not happen. Create a fake result.
local t = {}
t.HitPos = pos + pos2
return t
end
t.HitPos = t.HitPos or (pos + pos2)
return t
end
local function AddPrint(ent,foot)
-- Foot calc
local velspeed = ent:GetVelocity():Length()
local y = rad(ent:GetAngles().y)
local fy = y + rad((foot * 2 - 1) * -90)
local l = 5 * ent:GetModelScale()
local ex = Vector(cos(fy) * l + cos(y) * l,sin(fy) * l + sin(y) * l,0)
local pos = ent:GetPos() + ex
-- Find impact
local tr = ET(pos + Vector(0,0,20),Vector(0,0,-40),MASK_SOLID_BRUSHONLY,ent)
if not tr.Hit then return end -- In space?
-- If no bone_angle then angle math
local normal = -tr.HitNormal
-- CalcAng
local yawoff
if ent:IsPlayer() then
yawoff = normal:Angle().y - ent:EyeAngles().y + 180
else
yawoff = normal:Angle().y - ent:GetAngles().y + 180
end
table.insert(prints,{tr.HitPos, normal,foot,ent:GetModelScale() or 1,CurTime() + footstep_maxlife,clamp(velspeed / 300,1,2),yawoff})
-- pos, normal,foot,scale, life, lengh, yawoff
end
-- Footprint logic
local BL = {"npc_hunter","monster_bigmomma","npc_vortigaunt","npc_dog","npc_fastzombie","npc_stalker"} -- Blacklist footprints
local function CanPrint(ent)
local c = ent:GetClass()
for i,v in ipairs(BL) do
if find(c, v,1,true) then return false end
end
if find(ent:GetModel(),"_torso",1,true) then return false end
return true
end
hook.Add("StormFox2.terrain.footstep", "StormFox2.terrain.makefootprint", function(ent, foot, sSnd, sTexture, bReplace )
if foot < 0 then return end -- Invalid foot
if not bReplace and bAlwaysFootstep then -- This is a cold map, check for snow
if not find(sTexture:lower(),"snow",1,true) then return end
elseif bReplace then -- This is terrain
local cT = StormFox2.Terrain.GetCurrent()
if not cT then return end
if not cT.footprints then return end
else -- Invalid
return
end
if not CanPrint(ent) then return end
if not StormFox2.Setting.GetCache("footprint_enabled",true) then return end
if StormFox2.Setting.GetCache("footprint_playeronly",false) and not ent:IsPlayer() then return end
local n_max = StormFox2.Setting.GetCache("footprint_max",200)
if #prints > n_max then
table.remove(prints, 1)
end
AddPrint(ent,foot)
end)
-- Footprint render
local mat = {Material("stormfox2/effects/foot_hq.png"),Material("stormfox2/effects/foot_hql.png"),Material("stormfox2/effects/foot_m.png"),Material("stormfox2/effects/foot_s.png")}
local function getMat(q,foot)
if q == 1 then
if foot == 0 then
return mat[2]
else
return mat[1]
end
end
return mat[q + 1]
end
local DrawQuadEasy = render.DrawQuadEasy
local bC = Color(0,0,0,255)
hook.Add("PreDrawOpaqueRenderables","StormFox2.Terrain.Footprints",function()
if not StormFox2.Setting.GetCache("footprint_enabled",true) then return end
if #prints < 1 then return end
local lp = StormFox2.util.RenderPos()
local del = {}
local footstep_dis = StormFox2.Setting.GetCache("footprint_distance",2000,"The renderdistance for footprints") ^ 2
for k,v in pairs(prints) do
local pos,normal,foot,scale,life,lengh,yawoff = v[1],v[2],v[3],v[4],v[5],v[6],v[7]
local blend = life - CurTime()
if blend <= 0 then
table.insert(del,k)
else
local q = min(ceil(lp:DistToSqr(pos) / footstep_dis),4)
if q >= 4 then continue end
render.SetMaterial(getMat(q,foot))
if foot == 0 and q > 1 then
DrawQuadEasy( pos + Vector(0,0,q / 3 + 1), normal, 6 * scale, 10 * scale * lengh, bC, yawoff )
else
DrawQuadEasy( pos + Vector(0,0,q / 3), normal, -6 * scale, 10 * scale * lengh, bC, yawoff )
end
end
end
for i = #del,1,-1 do
table.remove(prints,del[i])
end
end)
end
-- If the map is cold or has snow, always check for footsteps.
bAlwaysFootstep = StormFox2.Map.IsCold() or StormFox2.Map.HasSnow() -- This is a cold map.
if CLIENT then
StormFox2.Setting.AddCL("footprint_enabled",true) -- Add footprint setting
StormFox2.Setting.AddCL("footprint_max",200) -- Add footprint setting
StormFox2.Setting.AddCL("footprint_distance",2000) -- Add footprint setting
StormFox2.Setting.AddCL("footprint_playeronly",false) -- Add footprint setting
end
StormFox2.Setting.AddSV("footprint_enablelogic",true)

View File

@@ -0,0 +1,122 @@
--[[
| 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.Setting.AddSV("edit_tonemap", false, nil, "Effects")
if CLIENT then return end
StormFox2.ToneMap = {}
--SetBloomScale
-- On load data
local function LoadSettings()
-- Locate tonemap
local t = StormFox2.Map.FindClass("env_tonemap_controller")
if #t < 1 then return end -- Unable to locate tonemap within BSP
local targetname = t[1].targetname
if not targetname then return end -- This tonemap can't have any settings
-- Search for logic_auto
local tab = {}
for k, v in ipairs( StormFox2.Map.FindClass("logic_auto") ) do
if not v.onmapspawn then continue end -- No setting?
if not string.match(v.onmapspawn, "^" .. targetname .. ",") then continue end -- This targets tonemap.
for s in string.gmatch(v.raw, '"OnMapSpawn"%s?"' .. targetname .. ',(.-)"') do
local t = string.Explode(",", s)
tab[t[1]] = t[2]
end
end
return tab
end
local DefaultSettings = LoadSettings()
local ent
local changed = false
hook.Add("StormFox2.PostEntityScan", "StormFox2.ToneMapFind", function()
ent = StormFox2.Ent.env_tonemap_controller and StormFox2.Ent.env_tonemap_controller[1]
end)
do
local last = 1
---Sets the tonemaps bloomscale. Use at own rist ask it looks like Soure engine doesn't like it.
---@param num number
---@server
function StormFox2.ToneMap.SetBloomScale( num )
if not ent or not DefaultSettings or not DefaultSettings.SetBloomScale then return end
if last == num then return end
ent:Fire("SetBloomScale",DefaultSettings.SetBloomScale * num)
changed = true
last = num
end
end
do
local last = 1
---Sets the tonemaps exposure scale. Use at own rist ask it looks like Soure engine doesn't like it.
---@param num number
---@server
function StormFox2.ToneMap.SetExposureScale( num )
if not ent or not DefaultSettings then return end
if last == num then return end
ent:Fire("SetAutoExposureMax",(DefaultSettings.SetAutoExposureMax or 1) * num)
ent:Fire("SetAutoExposureMin",(DefaultSettings.SetAutoExposureMin or 0) * num)
changed = true
last = num
end
end
do
local last = 1
---Sets the tonemaps rate-scale. Use at own rist ask it looks like Soure engine doesn't like it.
---@param num number
---@server
function StormFox2.ToneMap.SetTonemapRateScale( num )
if not ent or not DefaultSettings then return end
if last == num then return end
ent:Fire("SetTonemapRate",(DefaultSettings.SetTonemapRate or 0.1) * num)
changed = true
last = num
end
end
---Resets the tonemap settings applied.
---@server
function StormFox2.ToneMap.Reset()
if not changed or not ent then return end
changed = false
StormFox2.ToneMap.SetBloomScale( 1 )
StormFox2.ToneMap.SetExposureScale( 1 )
StormFox2.ToneMap.SetTonemapRateScale( 1 )
end
local function getMaxLight()
local c = StormFox2.Weather.Get("Clear")
return c:Get("mapDayLight",80)
end
local function ToneMapUpdate( lightlvlraw )
if not StormFox2.Setting.SFEnabled() or not StormFox2.Setting.GetCache("edit_tonemap", true) then
StormFox2.ToneMap.Reset()
else
StormFox2.ToneMap.SetExposureScale( lightlvlraw / 100 )
end
end
local last_Raw = 100
-- Toggle tonemap with setting
StormFox2.Setting.Callback("edit_tonemap",function()
ToneMapUpdate(last_Raw)
end,"sf_edit_tonemap")
-- Save the last raw-lightlvl and update the tonemap
hook.Add("StormFox2.lightsystem.new", "StormFox2.ToneMap-Controller", function(lightlvl, lightlvl_raw)
last_Raw = lightlvl_raw
ToneMapUpdate(lightlvl_raw)
end)

View File

@@ -0,0 +1,205 @@
--[[
| 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/
--]]
-- Delete old skybox brushes
if SERVER then
hook.Add( "InitPostEntity", "DeleteBrushNEntity", function()
for i, ent in ipairs( ents.GetAll() ) do
if not IsValid(ent) then continue end
if ent:GetClass() == "func_brush" and (ent:GetName() or "") == "daynight_brush" then
SafeRemoveEntity(ent)
elseif ent:CreatedByMap() and (ent:GetModel() or "") == "models/props/de_port/clouds.mdl" then
ent:SetNoDraw( true )
end
end
end )
end
-- Foliage overwrite
StormFox2.Setting.AddSV("override_foliagesway",true,nil, "Effects")
if StormFox2.Setting.Get("override_foliagesway", true) and CLIENT then
--[[
Foliage_type:
-2 - No treesway
-1 - Tree trunk
0 - Tree / w branches andor leaves
1 - Branches / Leaves
2 - Ground Plant
Bendyness multiplier:
1 - default
mat_height:
0 - height
WaveBonus_speed:
<number>
]]
local default_foliage = {}
default_foliage["models/msc/e_leaves"] = {1}
default_foliage["models/msc/e_leaves2"] = {1}
default_foliage["models/msc/e_bark3"] = {-1}
default_foliage["models/trees/japanese_tree_bark_02"] = {-1, 0.5}
default_foliage["models/trees/japanese_tree_round_02"] = {1}
default_foliage["models/trees/japanese_tree_round_03"] = {1}
default_foliage["models/trees/japanese_tree_round_05"] = {1}
default_foliage["models/props_foliage/tree_deciduous_01a_leaves2"] = {1}
default_foliage["models/msc/e_bigbush3"] = {2,4}
default_foliage["models/props_coalmine/foliage1"] = {2}
default_foliage["models/props_foliage/mall_trees_branches03"] = {2}
default_foliage["models/props_foliage/tree_deciduous_01a_branches"] = {2}
default_foliage["models/props_foliage/bramble01a"] = {2,0.4}
default_foliage["models/props_foliage/leaves_bushes"] = {2}
default_foliage["models/props_foliage/leaves"] = {2}
default_foliage["models/props_foliage/cane_field01"] = {2,nil,0.3}
--default_foliage["models/props_foliage/cattails"] = {2} Not working
--default_foliage["models/props_foliage/trees_farm01"] = {-1,0.8,0.02,1.5} Doesn't look good on some trees
default_foliage["models/props_foliage/cedar01_mip0"] = {0,0.4,0.02,3}
default_foliage["models/props_foliage/coldstream_cedar_bark"] = {-1}
default_foliage["models/props_foliage/coldstream_cedar_branches"] = {0}
default_foliage["models/props_foliage/urban_trees_branches03"] = {0}
default_foliage["models/props_foliage/bush"] = {2}
default_foliage["models/props_foliage/corn_plant01"] = {1,3.4}
default_foliage["models/props_foliage/detail_clusters"] = {2}
default_foliage["models/cliffs/ferns01"] = {0,2,nil,2}
default_foliage["models/props_foliage/rocks_vegetation"] = {0,4,nil,1,2}
default_foliage["models/props_foliage/flower_barrel"] = {0,3,0.07,2}
default_foliage["models/props_foliage/flower_barrel_dead"] = {0,1,0.07,2}
default_foliage["models/props_foliage/flower_barrel_dead"] = {0,1,0.07,2}
default_foliage["models/props/de_inferno/flower_barrel"] = {0,3,0.02,2}
default_foliage["models/props_foliage/grass_01"] = {2,0.5}
default_foliage["models/props_foliage/grass_02"] = {2,0.5}
default_foliage["models/props_foliage/grass_clusters"] = {2}
default_foliage["models/props_foliage/urban_trees_branches02_mip0"] = {-1}
default_foliage["models/props_foliage/hedge_128"] = {2,0.8}
default_foliage["models/props_foliage/foliage1"] = {2}
default_foliage["models/props_foliage/hr_f/hr_medium_tree_color"] = {-1}
default_foliage["models/props_foliage/ivy01"] = {2,0.1}
default_foliage["models/props_foliage/mall_trees_branches01"] = {0,1,nil,2}
default_foliage["models/props_foliage/mall_trees_barks01"] = {-1,1,nil,4}
default_foliage["models/props_foliage/mall_trees_branches02"] = {-1,1,nil,4}
--default_foliage["models/props_foliage/oak_tree01"] = {}
default_foliage["models/props_foliage/potted_plants"] = {0,4,0.055}
default_foliage["models/props_foliage/shrub_03"] = {2}
default_foliage["models/props_foliage/shrub_03_skin2"] = {2}
default_foliage["models/props_foliage/swamp_vegetation01"] = {-1,0.005,0.2}
default_foliage["models/props_foliage/swamp_branches"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_branches01_large"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_barks_large"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_barks"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_branches01"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_barks_still"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_trees_barks_generic"] = {0,0.005,0.2,10}
default_foliage["models/props_foliage/swamp_shrubwall01"] = {2}
default_foliage["models/props_foliage/swamp_trees_branches01_alphatest"] = {0,0.05}
default_foliage["models/props_foliage/swamp_trees_branches01_still"] = {0,0.05}
default_foliage["models/props_foliage/branch_city"] = {-1}
default_foliage["models/props_foliage/arbre01"] = {-1,0.4,0.04,2}
default_foliage["models/props_foliage/arbre01_b"] = {-1,0.05,nil,2}
default_foliage["models/props_foliage/tree_deciduous_01a-lod.mdl"] = {}
default_foliage["models/props_foliage/tree_deciduous_01a_lod"] = {-1}
default_foliage["models/props_foliage/tree_pine_01_branches"] = {-2} -- Looks bad. Remove.
default_foliage["models/props_foliage/pine_tree_large"] = {-1,0.8}
default_foliage["models/props_foliage/pine_tree_large_snow"] = {-1,0.8}
default_foliage["models/props_foliage/branches_farm01"] = {-1,0.2,0.8}
default_foliage["models/props_foliage/urban_trees_branches03_small"] = {2,0.8}
default_foliage["models/props_foliage/urban_trees_barks01_medium"] = {-1}
default_foliage["models/props_foliage/urban_trees_branches03_medium"] = {0,2}
default_foliage["models/props_foliage/urban_trees_barks01_medium"] = {-1,2,0.2}
default_foliage["models/props_foliage/urban_trees_branches02_small"] = {2}
default_foliage["models/props_foliage/urban_trees_barks01_clusters"] = {-1,0.2,0.2}
default_foliage["models/props_foliage/urban_trees_branches01_clusters"] = {0,0.2,0.2}
default_foliage["models/props_foliage/urban_trees_barks01"] = {-1,0.2}
default_foliage["models/props_foliage/urban_trees_barks01_dry"] = {2,nil,10}
default_foliage["models/props_foliage/leaves_large_vines"] = {0}
default_foliage["models/props_foliage/vines01"] = {2,0.3}
default_foliage["models/map_detail/foliage/foliage_01"] = {2,0.5}
default_foliage["models/map_detail/foliage/detailsprites_01"] = {2}
default_foliage["models/nita/ph_resortmadness/pg_jungle_plant"] = {0,1.2}
default_foliage["models/nita/ph_resortmadness/plant_03"] = {-1,0.3}
default_foliage["models/nita/ph_resortmadness/leaf_8"] = {0,2}
default_foliage["models/nita/ph_resortmadness/fern_2"] = {0,2}
default_foliage["models/nita/ph_resortmadness/tx_plant_02"] = {0,4,nil,4}
default_foliage["models/nita/ph_resortmadness/tx_plant_04"] = {0,4,nil,4}
default_foliage["models/nita/ph_resortmadness/orchid"] = {0,4,nil,4}
default_foliage["models/props_foliage/ah_foliage_sheet001"] = {2,0.4}
default_foliage["models/props_foliage/ah_apple_bark001"] = {2,0.4}
default_foliage["statua/nature/furcard1"] = {2,0.1}
default_foliage["models/statua/shared/furcard1"] = {2,0.1}
local max = math.max
local function SetFoliageData(texture,foliage_type,bendyness,mat_height,wave_speed)
if not texture then return end
if not wave_speed then wave_speed = 0 end
if not bendyness then bendyness = 1 end
if not mat_height then mat_height = 0 end
local mat = Material(texture)
if mat:IsError() then return end -- This client don't know what the material this is
-- Enable / Disable the material
if foliage_type < -1 then
mat:SetInt("$treeSway",0)
return
end
mat:SetInt("$treeSway",1) -- 0 is no sway, 1 is classic tree sway, 2 is an alternate, radial tree sway effect.
-- 'Default' settings
mat:SetFloat("$treeswayspeed",2) -- The treesway speed
mat:SetFloat("$treeswayspeedlerpstart",1000) -- Sway starttime
-- Default varables I don't know what do or doesn't have much to do with cl_tree_sway_dir
mat:SetFloat("$treeswayscrumblefalloffexp",3)
mat:SetFloat("$treeswayspeedhighwindmultiplier",0.2)
mat:SetFloat("$treeswaystartradius",0)
mat:SetFloat("$treeswayscrumblefrequency",6.6)
mat:SetFloat("$treeswayspeedlerpend",2500 * bendyness)
-- Special varables
if foliage_type == -1 then --Trunk
mat:SetFloat("$treeSwayStartHeight",mat_height) -- When it starts to sway
mat:SetFloat("$treeswayheight",max(700 - bendyness * 100,0)) -- << How far up before XY starts to matter
mat:SetFloat("$treeswayradius",max(110 - bendyness * 10,0)) -- ?
mat:SetFloat("$treeswayscrumblespeed",3 + (wave_speed or 0)) -- ?
mat:SetFloat("$treeswayscrumblestrength",0.1 * bendyness) -- "Strechyness"
mat:SetFloat("$treeswaystrength",0) -- "Strechyness"
elseif foliage_type == 0 then -- Trees
mat:SetFloat("$treeSwayStartHeight",mat_height) -- When it starts to sway
mat:SetFloat("$treeswayheight",max(700 - bendyness * 100,0)) -- << How far up before XY starts to matter
mat:SetFloat("$treeswayradius",max(110 - bendyness * 10,0)) -- ?
mat:SetFloat("$treeswayscrumblespeed",3 + (wave_speed or 0) ) -- ?
mat:SetFloat("$treeswayscrumblestrength",0.1 * bendyness) -- "Strechyness"
mat:SetFloat("$treeswaystrength",0) -- ?
elseif foliage_type == 1 then -- Leaves
mat:SetFloat("$treeSwayStartHeight",0.5 + mat_height / 2)
mat:SetFloat("$treeswayheight",8)
mat:SetFloat("$treeswayradius",1)
mat:SetFloat("$treeswayscrumblespeed",1 + (wave_speed or 0))
mat:SetFloat("$treeswayscrumblestrength",0.1)
mat:SetFloat("$treeswaystrength",0.06 * bendyness)
else
mat:SetFloat("$treeSwayStartHeight",0.1 + mat_height / 10)
mat:SetFloat("$treeswayheight",8)
mat:SetFloat("$treeswayradius",1)
mat:SetFloat("$treeswayscrumblespeed",wave_speed or 0)
mat:SetFloat("$treeswayscrumblestrength",0)
mat:SetFloat("$treeswaystrength",0.05 * bendyness)
end
mat:SetFloat("treeswaystatic", 0)
end
hook.Add("stormfox2.postinit", "stormfox2.treeswayinit", function()
for texture,data in pairs(default_foliage) do
if not data or #data < 1 then continue end
if data[1] < -1 then
SetFoliageData(texture,-2)
else
SetFoliageData(texture,unpack(data))
end
end
end)
end

View File

@@ -0,0 +1,40 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
vFire support :D
---------------------------------------------------------------------------]]
local vFireList = {}
hook.Add("vFireOnCalculateWind","vFire - StormFox Handshake",function(vFireEnt)
local outside = StormFox2.Wind.IsEntityInWind(vFireEnt)
if outside then
vFireList[vFireEnt] = true
return StormFox2.Wind.GetVector() / 20
end
end)
if CLIENT then return end
local ran = math.random
timer.Create("vFire - StormFox Rain",2,0,function()
local r = StormFox2.Weather.GetRainAmount()
if r <= 0 then table.Empty(vFireList) return end
for ent,_ in pairs(vFireList) do
if IsValid(ent) then
ent:SoftExtinguish(r * ran(130,160))
end
end
table.Empty(vFireList)
end)
timer.Simple(2,function()
if not vFireInstalled then return end
StormFox2.Msg("Gee, vFire, what do you want to do tonight?")
hook.Call("vFire - StormFox Handeshake")
end)

View File

@@ -0,0 +1,223 @@
--[[
| 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/
--]]
--[[-------------------------------------------------------------------------
Light entities: ( env_projectedtexture , light_dynamic, light, light_spot )
Requirements:
- Named "night" or "1" or "day".
- Not have the name "indoor".
logic_relays support and map lights.
dusk / night_events
dawn / day_events
weather_<type> Called when a weathertype gets applied
weather_onchange Called when a weathertype changes
weather_<type>_off Called when a weathertype gets removed
---------------------------------------------------------------------------]]
-- Special Relays
local special_relays = {}
special_relays.day_relays = {}
special_relays.daytime_relays = {}
special_relays.night_relays = {}
special_relays.nighttime_relays = {}
special_relays.weather_on = {}
special_relays.weather_off = {}
local scanned = false
local startingQ = {}
hook.Add("StormFox2.InitPostEntity", "StormFox2.MapInteractions.SRelays", function()
for _, ent in ipairs( ents.GetAll() ) do
if not ent:IsValid() then continue end
local class = ent:GetClass()
if class == "logic_day_relay" then
if ent:GetTriggerType() == 0 then -- Light change
table.insert(special_relays.day_relays, ent)
else
table.insert(special_relays.daytime_relays, ent)
end
elseif class == "logic_night_relay" then
if ent:GetTriggerType() == 0 then -- Light change
table.insert(special_relays.night_relays, ent)
else
table.insert(special_relays.nighttime_relays, ent)
end
elseif class == "logic_weather_relay" then
table.insert(special_relays.weather_on, ent)
elseif class == "logic_weather_off_relay" then
table.insert(special_relays.weather_off, ent)
end
end
scanned = true
for k, ent in ipairs( startingQ ) do
if not ent:IsValid() then continue end
ent:Trigger()
end
startingQ = {}
end)
local function triggerAll(tab)
if not scanned then -- Wait until all entities are loaded
for _, ent in ipairs(tab) do
if not ent:IsValid() then continue end
table.insert(startingQ, ent)
end
return
end
for _, ent in ipairs(tab) do
if not ent:IsValid() then continue end
ent:Trigger()
end
end
local night_lights = {{}, {}, {}, {}, {}, {}}
local relays = {}
local switch
-- local functions
local function setELight( ent, bTurnOn )
local sOnOff = bTurnOn and "TurnOn" or "TurnOff"
ent:Fire( sOnOff )
end
local function setLights( bTurnOn )
if timer.Exists("StormFox2.mi.lights") then
timer.Remove("StormFox2.mi.lights")
end
local i = 1
timer.Create("StormFox2.mi.lights", 0.5, 6, function()
for _,ent in ipairs(night_lights[i] or {}) do
if not IsValid(ent) then continue end
setELight(ent, bTurnOn)
end
i = i + 1
end)
end
local function SetRelay(fMapLight)
local lights_on = fMapLight < 20
if switch ~= nil and lights_on == switch then return end -- Nothing changed
if lights_on then
StormFox2.Map.CallLogicRelay("night_events")
triggerAll(special_relays.night_relays)
setLights( true )
else
StormFox2.Map.CallLogicRelay("day_events")
triggerAll(special_relays.day_relays)
setLights( false )
end
switch = lights_on
end
local includeNames = {
["1"] = true,
["streetlight"] = true,
["streetlights"] = true
}
local includeSearch = {
["night"] = true,
["day"] = true,
-- ["lake"] = true, Used indoors .. for some reason
["outdoor"] = true
}
local excludeSearch = {
["indoor"] = true,
["ind_"] = true,
["apt_"] = true
}
local function Search(name, tab)
for _, str in ipairs( tab ) do
if string.format(name, str) then return true end
end
return false
end
local t = {"env_projectedtexture", "light_dynamic", "light", "light_spot"}
hook.Add("StormFox2.InitPostEntity", "StormFox2.lightioinit", function()
-- Locate lights on the map
for i,ent in ipairs( ents.GetAll() ) do
local c = ent:GetClass()
if not table.HasValue(t, c) then continue end
local name = ent:GetName()
-- Unnamed entities
if not name then
if c == "light_spot" then
table.insert(night_lights[ 1 + i % 6 ],ent)
end
continue
end
name = name:lower()
-- Check for include
if includeNames[name] then
table.insert(night_lights[ 1 + i % 6 ],ent)
continue
end
-- Check exclude
if Search(name, excludeSearch) then
continue
end
-- Check include
if not Search(name, includeSearch) then
continue
end
table.insert(night_lights[ 1 + i % 6 ],ent)
end
-- Update on launch
timer.Simple(5, function()
SetRelay(StormFox2.Map.GetLight())
end)
end)
-- Call day and night relays
hook.Add("StormFox2.lightsystem.new", "StormFox2.mapinteractions.light", SetRelay)
-- Call day andn ight time-related relays
hook.Add("StormFox2.Time.OnDay", "StormFox2.mapinteractions.day", function()
triggerAll( special_relays.daytime_relays )
end)
hook.Add("StormFox2.Time.OnNight", "StormFox2.mapinteractions.night", function()
triggerAll( special_relays.nighttime_relays )
end)
local function getRelayName( )
local c_weather = StormFox2.Weather.GetCurrent()
local relay = c_weather.Name
if c_weather.LogicRelay then
relay = c_weather.LogicRelay() or relay
end
return relay
end
-- StormFox2.Map.w_CallLogicRelay( name )
local lastWeather
local function checkWRelay()
local relay = getRelayName()
relay = string.lower(relay)
if lastWeather and lastWeather == relay then return end -- Nothing changed
StormFox2.Map.w_CallLogicRelay( relay )
local wP = StormFox2.Data.GetFinal("w_Percentage") or 0
for k,ent in ipairs(special_relays.weather_on) do
if ent:GetRequiredWeather() ~= relay then continue end
if not ent:HasRequredAmount() then continue end
ent:Trigger()
end
for k,ent in ipairs(special_relays.weather_off) do
if ent:GetRequiredWeather() ~= lastWeather then continue end
ent:Trigger()
end
lastWeather = relay
end
hook.Add("StormFox2.weather.postchange", "StormFox2.mapinteractions" , function( sName ,nPercentage )
timer.Simple(1, checkWRelay)
end)
hook.Add("StormFox2.data.change", "StormFox2.mapinteractions.w_logic", function(sKey, nDay)
if sKey ~= "Temp" then return end
timer.Simple(1, checkWRelay)
end)

View File

@@ -0,0 +1,17 @@
--[[
| 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/
--]]
net.Receive(StormFox2.Net.Texture, function(len, ply)
StormFox2.Permission.EditAccess(ply,"StormFox Settings", function()
StormFox2.Map.ModifyMaterialType( net.ReadString(), net.ReadInt( 3 ))
end)
end)

View File

@@ -0,0 +1,901 @@
--[[
| 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 auto_weather = StormFox2.Setting.AddSV("auto_weather",true,nil, "Weather")
local hide_forecast = StormFox2.Setting.AddSV("hide_forecast",false,nil, "Weather")
util.AddNetworkString( "StormFox2.weekweather" )
local forecast = {} -- The forecast table
local function SetForecast( tab, unix_time )
forecast = tab
forecast.unix_time = unix_time
if not hide_forecast then return end
net.Start("StormFox2.weekweather")
net.WriteBool( unix_time )
net.WriteTable( tab.temperature or {} )
net.WriteTable( tab.weather or {} )
net.WriteTable( tab.wind or {} )
net.WriteTable( tab.windyaw or {} )
net.Broadcast()
end
---Returns the forecast data
---@return table
---@server
function StormFox2.WeatherGen.GetForecast()
return forecast
end
---Returns true if we're using unix time for the forecast.
---@return boolean
---@server
function StormFox2.WeatherGen.IsUnixTime()
return forecast.unix_time or false
end
-- Open Weather API Settings
local api_MaxCalls = 59
local KEY_INVALID = 0
local KEY_UNKNOWN = 1
local KEY_VALID = 2
local KEY_STATUS = KEY_UNKNOWN
local API_ENABLE = StormFox2.Setting.AddSV("openweathermap_enabled",false,nil,"Weather")
StormFox2.Setting.AddSV("openweathermap_lat",52,nil,"Weather",-180,180) -- Fake setting
StormFox2.Setting.AddSV("openweathermap_lon",-2,nil,"Weather",-180,180) -- Fake setting
if SERVER then
CreateConVar("sf_openweathermap_key", "", {FCVAR_ARCHIVE, FCVAR_PROTECTED}, "Sets the API key")
CreateConVar("sf_openweathermap_real_lat","52.613909" , {FCVAR_ARCHIVE, FCVAR_PROTECTED}, "The real LAT for the API")
CreateConVar("sf_openweathermap_real_lon","-2.005960" , {FCVAR_ARCHIVE, FCVAR_PROTECTED}, "The real LON for the API")
end
local key = StormFox2.Setting.AddSV("openweathermap_key", "", nil,"Weather")
local location = StormFox2.Setting.AddSV("openweathermap_location", "", nil,"Weather") -- Fake setting
local city = StormFox2.Setting.AddSV("openweathermap_city","",nil,"Weather") -- Fake setting
-- Keep them secret and never network them.
key.isSecret = true
location.isSecret = true
city.isSecret = true
local function onSuccessF( body, len, head, code )
KEY_STATUS = KEY_VALID
if not auto_weather:GetValue() then return end
-- Most likly an invalid API-Key.
local t = util.JSONToTable(body) or {}
if code == 401 then
KEY_STATUS = KEY_INVALID
StormFox2.Warning(t.message or "API returned 401! Check your OpenWeatherMap account.")
StormFox2.Setting.Set("openweathermap_enabled", false)
return
end
if t.cod == "404" then return end -- Not found
local timeZone = t.timezone and tonumber(t.timezone) or 0
-- We can set the sunrise and sunset
if t.sys and t.sys.sunrise and t.sys.sunset then
local sunrise = os.date("!%H:%M",t.sys.sunrise + timeZone)
local sunset = os.date("!%H:%M",t.sys.sunset + timeZone)
StormFox2.Setting.Set("sunrise",StormFox2.Time.StringToTime(sunrise))
StormFox2.Setting.Set("sunset",StormFox2.Time.StringToTime(sunset))
end
if t.main then
-- Temperature
local temp = StormFox2.Temperature.Convert("kelvin",nil,tonumber( t.main.temp or t.main.temp_min or t.main.temp_max ))
StormFox2.Temperature.Set( temp, 1 )
-- Weather
local cloudyness = ( t.clouds and t.clouds.all or 0 ) / 110
local rain = 0
if t.rain then
rain = math.max( t.rain["1h"] or 0, t.rain["3h"] or 0, 2) / 8
elseif t.snow then
rain = math.max( t.snow["1h"] or 0, t.snow["3h"] or 0, 2) / 8
end
if rain > 0 then
StormFox2.Weather.Set("Rain", math.Round(rain * .7 + 0.2,2))
elseif cloudyness >= 0.1 then
StormFox2.Weather.Set("Cloud", math.Round(cloudyness, 2))
else
StormFox2.Weather.Set("Clear", 1)
end
-- Thunder
local b_thunder = false
if t.weather and t.weather[1] and t.weather[1].id and (rain > 0 or cloudyness >= 0.3) then
local id = t.weather[1].id
b_thunder = ( id >= 200 and id <= 202 ) or ( id >= 210 and id <= 212 ) or ( id >= 230 and id <= 232 ) or id == 212
end
StormFox2.Thunder.SetEnabled(b_thunder, id == 212 and 12 or 6) -- 212 is heavy thunderstorm
-- Wind
StormFox2.Wind.SetForce( t.wind and t.wind.speed or 0 )
StormFox2.Wind.SetYaw( t.wind and t.wind.deg or 0 )
end
end
local n_NextAllowedCall = 0
local b_BlockNextW = false
local function UpdateLiveWeather( api_key )
if b_BlockNextW then return end
if KEY_STATUS == KEY_INVALID then return end
if n_NextAllowedCall >= CurTime() then
return StormFox2.Warning("API can't be called that often!")
end
n_NextAllowedCall = CurTime() + (60 / api_MaxCalls)
local lat = GetConVar("sf_openweathermap_real_lat"):GetString()
local lon = GetConVar("sf_openweathermap_real_lon"):GetString()
local api_key = api_key or GetConVar("sf_openweathermap_key"):GetString()
http.Fetch("http://api.openweathermap.org/data/2.5/weather?lat=" .. lat .. "&lon=" .. lon .. "&appid=" .. api_key, onSuccessF)
end
local function onSuccessForecast( body, len, head, code )
if KEY_STATUS == KEY_INVALID then return end
local t = util.JSONToTable(body) or {}
if code == 401 then
KEY_STATUS = KEY_INVALID
StormFox2.Warning(t.message or "API returned 401! Check your OpenWeatherMap account.")
StormFox2.Setting.Set("openweathermap_enabled", false)
return
end
if t.cod == "404" then return end -- Not found
if not t.list then return end -- ??
local forecast = {}
forecast.temperature= {}
forecast.weather = {}
forecast.wind = {}
forecast.windyaw = {}
local last_time = -1
local ex_time = 0
for k, v in ipairs( t.list ) do
if not v.main then continue end -- ERR
--local timestr = string.match(v.dt_txt, "(%d+:%d+:%d)")
--local c = string.Explode(":", timestr)
--local h, m, s = c[1] or "0", c[2] or "0", c[3] or "0"
--local time = ( tonumber( h ) or 0 ) * 60 + (tonumber( m ) or 0) + (tonumber( s ) or 0) / 60
--if time < last_time then -- New day
-- ex_time = ex_time + 1440
-- last_time = time
--else
-- last_time = time
--end
-- New time: time + ex_time
--local timeStamp = time + ex_time
local timeStamp = v.dt
if timeStamp > 1440 then
if k%2 == 1 then
continue
end
elseif timeStamp > 2440 then
continue
end
local temp = StormFox2.Temperature.Convert("kelvin",nil,tonumber( v.main.temp or v.main.temp_min or v.main.temp_max ))
local cloudyness = ( t.clouds and t.clouds.all or 0 ) / 110
local rain = 0
local w_type = "Clear"
local w_procent = 0
if v.rain then
rain = math.max( v.rain["1h"] or 0, v.rain["3h"] or 0, 2) / 8
elseif v.snow then
rain = math.max( v.snow["1h"] or 0, v.snow["3h"] or 0, 2) / 8
end
if rain > 0 then
w_procent = math.Round(rain * .7 + 0.2,2)
w_type = "Rain"
elseif cloudyness > 0.1 then
w_procent = math.Round(cloudyness, 2)
w_type = "Cloud"
end
local b_thunder = false
if v.weather and v.weather[1] and v.weather[1].id and (rain > 0 or cloudyness >= 0.3) then
local id = v.weather[1].id
b_thunder = ( id >= 200 and id <= 202 ) or ( id >= 210 and id <= 212 ) or ( id >= 230 and id <= 232 ) or id == 212
end
local wind = v.wind and v.wind.speed or 0
local windyaw = v.wind and v.wind.deg or 0
table.insert(forecast.temperature, {timeStamp, temp})
table.insert(forecast.weather, {timeStamp, {
["sName"] = w_type,
["fAmount"] = w_procent
}})
table.insert(forecast.wind, {timeStamp, wind})
table.insert(forecast.windyaw, {timeStamp, windyaw})
end
SetForecast( forecast, true )
end
local function UpdateLiveFeed( api_key )
if KEY_STATUS == KEY_INVALID then return end
local lat = GetConVar("sf_openweathermap_real_lat"):GetString()
local lon = GetConVar("sf_openweathermap_real_lon"):GetString()
local api_key = api_key or GetConVar("sf_openweathermap_key"):GetString()
http.Fetch("http://api.openweathermap.org/data/2.5/forecast?lat=" .. lat .. "&lon=" .. lon .. "&appid=" .. api_key, onSuccessForecast)
end
local function SetCity( sCityName, callBack )
if KEY_STATUS == KEY_INVALID then return end
if n_NextAllowedCall >= CurTime() then
return StormFox2.Warning("API can't be called that often!")
end
n_NextAllowedCall = CurTime() + (60 / api_MaxCalls)
http.Fetch("http://api.openweathermap.org/data/2.5/weather?q=" .. sCityName .. "&appid=" .. GetConVar("sf_openweathermap_key"):GetString(), function( body, len, head, code )
-- Most likly an invalid API-Key.
local t = util.JSONToTable(body) or {}
if code == 401 then
KEY_STATUS = KEY_INVALID
StormFox2.Warning(t.message or "API returned 401")
StormFox2.Setting.Set("openweathermap_enabled", false)
return
end
if t.cod == 404 or not t.coord then -- City not found
if callBack then callBack( false ) end
return
end
b_BlockNextW = true -- Stop the setting from updating the weather again
local lat = tonumber( t.coord.lat )
RunConsoleCommand( "sf_openweathermap_real_lat", lat )
StormFox2.Setting.Set("openweathermap_lat",math.Round(lat)) -- Fake settings
local lon = tonumber( t.coord.lon )
RunConsoleCommand( "sf_openweathermap_real_lon", lon )
StormFox2.Setting.Set("openweathermap_lon",math.Round(lon)) -- Fake settings
b_BlockNextW = false
onSuccessF( body, len, head, code )
-- We found a city. Make the forecast
timer.Simple(1, UpdateLiveFeed)
if callBack then callBack( true ) end
end)
end
--- Update Value
key:AddCallback( function( sString )
RunConsoleCommand( "sf_openweathermap_key", sString )
key.value = "" -- Silent set it again
KEY_STATUS = KEY_UNKNOWN
UpdateLiveWeather( sString ) -- Try and set the weather
end)
location:AddCallback( function( sString )
local num = tonumber( string.match(sString, "[-%d]+") or "0" ) or 0
if sString:sub(0, 1) == "a" then
RunConsoleCommand( "sf_openweathermap_real_lat", num )
StormFox2.Setting.Set("openweathermap_lat",math.Round(num)) -- Fake settings
else
RunConsoleCommand( "sf_openweathermap_real_lon", num )
StormFox2.Setting.Set("openweathermap_lon",math.Round(num)) -- Fake settings
end
location.value = "" -- Silent set it again
UpdateLiveWeather() -- Set the weather to the given location
timer.Simple(1, UpdateLiveFeed)
end)
city:AddCallback( function( cityName )
if cityName == "" then return end
SetCity( cityName )
city.value = "" -- Silent set it again
end)
-- Enable and disable API
local status = false
local function EnableAPI()
if status then return end
timer.Create("SF_WGEN_API", 5 * 60, 0, function()
if not auto_weather:GetValue() then return end
if not status then return end
UpdateLiveWeather()
end)
timer.Simple(1, UpdateLiveFeed)
end
local function DisableAPI()
if not status then return end
timer.Destroy("SF_WGEN_API")
end
local function IsUsingAPI()
return status
end
-- Weather Gen Settings
local max_days_generate = 7
local min_temp = StormFox2.Setting.AddSV("min_temp",-10,nil,"Weather",-273.15)
local max_temp = StormFox2.Setting.AddSV("max_temp",20,nil, "Weather")
local max_wind = StormFox2.Setting.AddSV("max_wind",50,nil, "Weather")
local night_temp= 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,
["thunder"] = true,
["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
})
-- Create settings for weather-types.
local weather_setting = {}
local OnWeatherSettingChange
local function call( newVar, oldVar, sName )
sName = string.match(sName, "wgen_(.+)") or sName
weather_setting[sName] = SplitSetting( newVar )
OnWeatherSettingChange()
end
hook.Add("stormfox2.postloadweather", "StormFox2.WeatherGen.Load", function()
for _, sName in ipairs( StormFox2.Weather.GetAll() ) do
local str = default_setting[sName] or default
local obj = StormFox2.Setting.AddSV("wgen_" .. sName,str,nil,"Weather")
obj:AddCallback( call )
weather_setting[sName] = SplitSetting( obj:GetValue() )
end
end)
for _, sName in ipairs( StormFox2.Weather.GetAll() ) do
local str = default_setting[sName] or default
local obj = StormFox2.Setting.AddSV("wgen_" .. sName,str,nil,"Weather")
obj:AddCallback( call, "updateWSetting" )
weather_setting[sName] = SplitSetting( obj:GetValue() )
end
local function SetWeatherSetting(sName, tab)
if CLIENT then return end
StormFox2.Setting.SetValue("wgen_" .. sName, CombineSetting(tab))
end
-- Returns the lowest key that is higer than the inputed key.
-- Second return is the higest key that is lower than the inputed key.
local function getClosestKey( tab, key)
local s, ls
for k, v in ipairs( table.GetKeys(tab) ) do
if v < key then
if not ls or ls < v then
ls = v
end
else
if not s or s > v then
s = v
end
end
end
return s, ls or 0
end
local generator = {} -- Holds all the days
local day = {}
day.__index = day
local function CreateDay()
local t = {}
t._temperature = {}
t._wind = {}
t._windyaw = {}
t._weather = {}
setmetatable(t, day)
table.insert(generator, t)
if #generator > max_days_generate then
table.remove(generator, 1)
end
return t
end
local lastTemp
function day:SetTemperature( nTime, nCelcius )
-- I got no clue why it tries to set values outside, but clamp it here just in case.
nCelcius = math.Clamp(nCelcius, min_temp:GetValue(), max_temp:GetValue())
self._temperature[nTime] = nCelcius
lastTemp = nCelcius
return self
end
function day:GetTemperature( nTime )
return self._temperature[nTime]
end
local lastWind, lastWindYaw
local lastLastWind
function day:SetWind( nTime, nWind, nWindYaw )
self._wind[nTime] = nWind
self._windyaw[nTime] = nWindYaw
lastLastWind = lastWind
lastWind = nWind
lastWindYaw = nWindYaw
return self
end
function day:GetWind( nTime )
return self._wind[nTime], self._windyaw[nTime]
end
function day:SetWeather( sName, nStart, nDuration, nAmount, nThunder )
self._weather[nStart] = {
["sName"] = sName,
["nStart"] = nStart,
["nDuration"] = nDuration,
["fAmount"] = nAmount,
["nThunder"] = nThunder }
self._last = math.max(self._last or 0, nStart + nDuration)
end
function day:GetWeather( nTime )
return self._weather[nTime]
end
function day:GetWeathers()
return self._weather
end
function day:GetLastWeather()
return self._weather[self._last]
end
local function GetLastDay()
return generator[#generator]
end
local weatherWeekCount = {}
local function CanGenerateWeather( sName )
local count = weatherWeekCount[ sName ] or 0
local setting = weather_setting[ sName ]
if not setting then return false end -- Invalid weahter? Ignore this.
local pr_week = setting.pr_week or 0
if pr_week <= 0 then return false end -- Disabled
if pr_week < 1 then -- Floats between 0 and 1 is random.
pr_week = math.random(0, 1 / pr_week) <= 1 and 1 or 0
end
if count >= pr_week then return false end -- This weahter is reached max for this week
return true
end
local function SortList()
local t = {}
for _, sName in pairs(StormFox2.Weather.GetAll()) do
t[sName] = weatherWeekCount[sName] or 0
end
return table.SortByKey(t, true)
end
local function UpdateWeekCount()
weatherWeekCount = {}
for k, day in ipairs( generator ) do
for nTime, weather in pairs( day:GetWeathers() ) do
local sName = weather.sName
weatherWeekCount[sName] = (weatherWeekCount[sName] or 0) + 1
end
end
end
local atemp = math.random(-4, 4)
local nextWeatherOverflow = 0
local function GenerateDay()
local newDay = CreateDay()
UpdateWeekCount()
-- Handle temperature
do
local mi, ma = min_temp:GetValue(), max_temp:GetValue()
local ltemp = lastTemp or math.random(mi, ma)
local aftemp = -math.min(ltemp - mi, 12) -- The closer the temperature is to minimum, the lower min
local ahtemp = math.min(ma - ltemp, 12) -- The closer the temperature is to maximum, the lower max
atemp = atemp + math.Rand(-4, 4) -- How much the temperature goes up or down
atemp = math.Clamp(atemp, aftemp, ahtemp)
-- UnZero
local tempBoost = 7 - math.abs( ltemp + atemp )
if tempBoost > 0 then
if atemp >= 0 then
atemp = math.max(atemp + tempBoost, tempBoost)
else
atemp = math.min(atemp - tempBoost, -tempBoost)
end
end
-- Spikes
if math.random(10) > 8 then
-- Create a spike
if ltemp + atemp >= 0 then
atemp = mi / 2
else
atemp = ma / 2
end
end
-- New temp
local newMidTemp = math.Round(math.Clamp(ltemp + atemp, mi + 4, ma), 1)
-- Make the new temperature
local h = StormFox2.Sun.GetSunRise()
local n_temp = night_temp:GetValue() or -7
local sunDown = StormFox2.Sun.GetSunSet() + math.random(-180, 180) - 180
newDay:SetTemperature( sunDown, newMidTemp )
newDay:SetTemperature( h - 180, math.max(newMidTemp + math.random(n_temp / 2, n_temp), mi) )
lastTemp = newMidTemp -- To make sure night-temp, don't effect the overall temp
end
-- Handle wind
local newWind
do
--lastWind, lastWindYaw
if not lastWind then
lastWind = math.random(5)
lastWindYaw = math.random(360)
end
local buff = math.abs(atemp) - 4 -- Wind tent to increase the more temp changes. Also add a small negative modifier
if math.random(1, 50) >= 49 and buff > 4 then -- Sudden Storm
buff = buff + 10
end
local addforce = math.random(buff / 2, buff - math.abs(lastLastWind or 0))
newWind = math.min(max_wind:GetValue(), math.max(0, lastWind + addforce))
local yawChange = math.min(40, lastWind + addforce * 15)
newDay:SetWind( math.random(180, 1080), newWind, ( lastWindYaw + math.random(-yawChange, yawChange) ) % 360 )
end
-- Handle weather
local i = 3 -- Only generates 2 types of weathers pr day at max
local _last = nextWeatherOverflow -- The next empty-time of the day
for _, sName in ipairs( SortList() ) do
if _last >= 1440 then continue end -- This day is full of weathers. Ignore.
if sName == "Clear" and math.random(0, newWind) < newWind * 0.8 then -- Roll a dice between 0 and windForce. If dice is below 80%, try and find another weahter instead.
if atemp > 0 then -- Warm weather tent to clear up the weather
break
elseif atemp < 0 then -- Colder weather will form weather
continue
end
end
-- Check if weather is enabled, and we haven't reached max.
if not CanGenerateWeather( sName ) then continue end
local setting = weather_setting[sName]
local minS, maxS = setting.start_min, setting.start_max
local minL, maxL = setting.length_min, setting.length_max
if _last >= maxS then continue end -- This weather can't be generated this late.
i = i - 1
if i <= 0 then break end
local start_time = math.random(math.max(minS, _last), maxS)
local length_time = math.random(minL, maxL)
local amount = math.Rand(setting.amount_min, setting.amount_max)
local nThunder
if setting.thunder and amount > 0.5 and math.random(0, 10) > 7 then
nThunder = math.random(4,8)
end
newDay:SetWeather( sName, start_time, length_time, amount, nThunder )
_last = start_time + length_time
end
nextWeatherOverflow = math.max(0, _last - 1440)
end
local function GenerateWeek()
for i = 1, max_days_generate do
GenerateDay()
end
end
local enable = false
local function IsUsingWeatherGen()
return enable
end
local function TimeToIndex( tab )
local c = table.GetKeys( tab )
local a = {}
table.sort(c)
for i = 1, #c do
a[i] = {c[i], tab[c[i]]}
end
return a
end
local wGenList = {}
local function PushDayToList() -- Merges the 7 days into one long line. Its more stable this way
-- empty forecast
wGenList = {}
wGenList._temperature = {}
wGenList._wind = {}
wGenList._windyaw = {}
wGenList._weather = {}
local lastWType = "Clear"
local lastWTime = -100
for i = 1, 4 do
local f = {}
local day = generator[i]
for nTime, var in pairs( day._temperature ) do
wGenList._temperature[ nTime + (i - 1) * 1440 ] = var
end
for nTime, var in pairs( day._wind ) do
local nn = nTime + (i - 1) * 1440
wGenList._wind[ nn ] = var
wGenList._windyaw[ nn ] = day._windyaw[ nTime ]
end
for nTime, var in pairs( day._weather ) do
if var.sName == "Clear" then -- Ignore clear weathers. They're default.
lastWType = "Clear"
continue
end
local nTimeStart = nTime + (i - 1) * 1440
local nTimeMax = nTimeStart + math.Round(math.random(var.nDuration / 4, var.nDuration / 2), 1)
local nTimeMaxEnd = nTimeStart + var.nDuration * 0.75
local nTimeEnd = nTimeStart + var.nDuration
local wObj = StormFox2.Weather.Get(var.sName)
local t = {
["sName"] = var.sName,
["fAmount"] = math.Round(var.fAmount, 2),
["bThunder"] = var.nThunder
}
local useCloud = wObj.Inherit == "Cloud" and math.random(1, 10) >= 5
local startWType = useCloud and "Cloud" or var.sName
if lastWType == var.sName and lastWTime == nTimeStart then -- In case we had the same weather type before, remove the "fading out" part
wGenList._weather[ lastWTime ] = nil
startWType = var.sName
else
wGenList._weather[ nTimeStart ] = {
["sName"] = startWType,
["fAmount"] = 0
}
end
wGenList._weather[ nTimeMax ] = {
["sName"] = startWType,
["fAmount"] = math.Round(var.fAmount, 2)
}
wGenList._weather[ nTimeMaxEnd ] = t
wGenList._weather[ nTimeEnd ] = {
["sName"] = var.sName,
["fAmount"] = 0
}
lastWType = var.sName
lastWTime = var.nTimeEnd
end
end
-- Push it into an index
wGenList.weather = TimeToIndex( wGenList._weather )
wGenList.temperature = TimeToIndex( wGenList._temperature )
wGenList.wind = TimeToIndex( wGenList._wind )
wGenList.windyaw = TimeToIndex( wGenList._windyaw )
if not IsUsingWeatherGen() then return end -- Don't update the forecast. But keep the weather in mind in case it gets enabled.
SetForecast( wGenList )
end
-- In case settings change, update weekweather
local function ClearAndRedo()
timer.Simple(1, function()
weatherWeekCount = {}
generator = {}
weather_index = 0
wind_index = 0
temp_index = 0
-- Generate new Day
GenerateWeek()
-- Make WGen
PushDayToList()
end)
end
min_temp:AddCallback( ClearAndRedo, "weekWeather" )
max_temp:AddCallback( ClearAndRedo, "weekWeather" )
max_wind:AddCallback( ClearAndRedo, "weekWeather" )
night_temp:AddCallback( ClearAndRedo, "weekWeather" )
OnWeatherSettingChange = ClearAndRedo
local lastWeather, lastWind, lastTemp = -1, -1 , -1
local function fkey( x, a, b )
return (x - a) / (b - a)
end
local function findNext( tab, time ) -- First one is time
for i, v in ipairs( tab ) do
if time > v[1] then continue end
return i
end
return 0
end
local weather_index = 0
local wind_index = 0
local temp_index = 0
local function EnableWGenerator(forceCall)
if enable and not forceCall then return end
enable = true
GenerateWeek() -- Generate a week
PushDayToList() -- Push said list to w-table.
-- We need to set the start-weather
-- Set the start temperature
local curTime = math.ceil(StormFox2.Time.Get())
local t_index = findNext( wGenList.temperature, curTime )
if t_index > 0 then
local procentStart = 1
local _start = wGenList.temperature[t_index - 1]
local _end = wGenList.temperature[t_index]
if _start then
procentStart = fkey( curTime, _start[1], _end[1] )
end
local temp = Lerp( procentStart, (_start or _end)[2], _end[2] )
StormFox2.Temperature.Set( math.Round(temp, 2), 0 )
end
-- Set the start wind
local wind_index = findNext( wGenList.wind, curTime )
if wind_index > 0 then
local procentStart = 1
local _start = wGenList.wind[t_index - 1]
local _end = wGenList.wind[t_index]
if _start then
procentStart = fkey( curTime, _start[1], _end[1] )
end
local wind = Lerp( procentStart, (_start or _end)[2], _end[2] )
StormFox2.Wind.SetForce( math.Round(wind, 2), 0 )
local _start = wGenList.windyaw[t_index - 1]
local _end = wGenList.windyaw[t_index]
local windyaw = Lerp( procentStart, (_start or _end)[2], _end[2] )
StormFox2.Wind.SetYaw( math.Round(windyaw, 2), 0 )
end
-- Set the start weather
local weather_index = findNext( wGenList.weather, curTime )
if weather_index > 0 then
local procentStart = 1
local _start = wGenList.weather[weather_index - 1]
local _end = wGenList.weather[weather_index]
if _start then
procentStart = fkey( curTime, _start[1], _end[1] )
else
_start = _end
end
local isClear = _end[2].sName == "Clear" or _end[2].fAmount == 0
local w_type = ( isClear and _start[2] or _end[2] ).sName
local w_procent = ( isClear and _start[2] or _end[2] ).fAmount
StormFox2.Weather.Set( w_type, w_procent * procentStart )
if _end[2].bThunder then
StormFox2.Thunder.SetEnabled(true, _end[2].bThunder)
else
StormFox2.Thunder.SetEnabled(false, 0)
end
end
-- Create a timer to modify the weather, checks every 1.5 seconds
timer.Create("SF_WGEN_DEF", 0.5, 0, function()
local cT = StormFox2.Time.Get()
local e_index = findNext( wGenList.weather, cT )
local i_index = findNext( wGenList.wind, cT )
local t_index = findNext( wGenList.temperature, cT )
if weather_index~= e_index then
weather_index = e_index
local w_data = wGenList.weather[e_index]
if w_data then
local delta = StormFox2.Time.SecondsUntil(w_data[1])
StormFox2.Weather.Set(w_data[2].sName, w_data[2].fAmount, delta )
if w_data[2].bThunder then
StormFox2.Thunder.SetEnabled(true, w_data[2].bThunder)
else
StormFox2.Thunder.SetEnabled(false, 0)
end
end
end
if wind_index~= i_index then
wind_index = i_index
local i_data = wGenList.wind[i_index]
local y_data = wGenList.windyaw[i_index]
if i_data then
local secs = StormFox2.Time.SecondsUntil(i_data[1])
StormFox2.Wind.SetForce(i_data[2], secs)
if y_data then
StormFox2.Wind.SetYaw( y_data[2], secs)
end
end
end
if temp_index~= t_index then
temp_index = t_index
local t_data = wGenList.temperature[t_index]
if t_data then
local delta = StormFox2.Time.SecondsUntil(t_data[1])
StormFox2.Temperature.Set(t_data[2], delta )
end
end
end)
end
local function DisableWGenerator()
if not enable then return end
enable = false
timer.Destroy("SF_WGEN_DEF")
end
hook.Add("StormFox2.Time.NextDay", "StormFox2.WGen.ND", function()
if not enable then return end
-- Generate new Day
GenerateDay()
-- Make WGen
PushDayToList()
weather_index = 0
wind_index = 0
temp_index = 0
end)
-- Logic
local function NewWGenSetting()
if not auto_weather:GetValue() then -- Auto weather is off. Make sure API is off too
DisableAPI()
DisableWGenerator()
return
end
if API_ENABLE:GetValue() == true then
EnableAPI()
DisableWGenerator()
StormFox2.Msg("Using OpenWeatherMap API")
else
DisableAPI()
EnableWGenerator()
StormFox2.Msg("Using WeatherGen")
end
end
API_ENABLE:AddCallback( NewWGenSetting, "API_Enable")
auto_weather:AddCallback( NewWGenSetting, "WGEN_Enable")
hook.Add("stormfox2.postinit", "WGenInit", NewWGenSetting)
NewWGenSetting()
hook.Add("StormFox2.data.initspawn", "StormFox2.Weather.SendForcast",function( ply )
if not hide_forecast then return end
if not forecast then return end -- ?
net.Start("StormFox2.weekweather")
net.WriteBool( forecast.unix_time )
net.WriteTable( forecast.temperature or {} )
net.WriteTable( forecast.weather or {} )
net.WriteTable( forecast.wind or {} )
net.WriteTable( forecast.windyaw or {} )
net.Broadcast()
end)