mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
Upload
This commit is contained in:
69
lua/stormfox2/framework/cl_clientquality.lua
Normal file
69
lua/stormfox2/framework/cl_clientquality.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[
|
||||
| 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 scripts job is to sort out the computers and potatoes.
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
StormFox2.Client = StormFox2.Client or {}
|
||||
StormFox2.Setting.AddCL("quality_ultra",false)
|
||||
StormFox2.Setting.AddCL("quality_target",60,nil,nil, 0, 300)
|
||||
|
||||
local conDetect = 1
|
||||
local t_num = {1, 1, 1, 1, 1, 1}
|
||||
local i = 1
|
||||
local q_num = 1
|
||||
-- Calculate the avageFPS for the client and make a value we can use.
|
||||
local bi,buffer = 0,0
|
||||
local avagefps = 1 / RealFrameTime()
|
||||
timer.Create("StormFox2.Client.PotatoSupport",0.25,0,function()
|
||||
if not system.HasFocus() then -- The player tabbed out.
|
||||
bi,buffer = 0,0
|
||||
return
|
||||
end
|
||||
if bi < 10 then
|
||||
buffer = buffer + 1 / RealFrameTime()
|
||||
bi = bi + 1
|
||||
else
|
||||
avagefps = buffer / bi
|
||||
bi,buffer = 0,0
|
||||
local q = StormFox2.Setting.GetCache("quality_ultra",false)
|
||||
local delta_fps = avagefps - StormFox2.Setting.GetCache("quality_target",80)
|
||||
local delta = math.Clamp(delta_fps / 8,-3,3)
|
||||
conDetect = math.Clamp(math.Round(conDetect + delta, 1),0,q and 20 or 7)
|
||||
table.insert(t_num, conDetect)
|
||||
table.remove(t_num, 1)
|
||||
|
||||
local a = 0
|
||||
for _,v in ipairs( t_num ) do
|
||||
a = a + v
|
||||
end
|
||||
q_num = (q_num + (a / #t_num)) / 2
|
||||
end
|
||||
end)
|
||||
--[[<Client>-----------------------------------------------------------------
|
||||
Returns a number based on the clients FPS.
|
||||
7 is the max without the user enabling 'sf_quality_ultra', where it then goes up to 20.
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---Returns a number based on the clients FPS. Where 7 is max (or 20 if sf_quality_ultra is enabled)
|
||||
---@return number qualityNumber
|
||||
---@return number avagefps
|
||||
---@client
|
||||
function StormFox2.Client.GetQualityNumber()
|
||||
if not system.HasFocus() then
|
||||
return 1, 1 / RealFrameTime()
|
||||
end
|
||||
-- Players have complained not seeing the particles when their FPS is low
|
||||
--if game.SinglePlayer() then I've now had multiplayer complaints.
|
||||
q_num = math.max(0.5, q_num)
|
||||
--end
|
||||
return q_num, avagefps
|
||||
end
|
||||
1317
lua/stormfox2/framework/cl_envioment.lua
Normal file
1317
lua/stormfox2/framework/cl_envioment.lua
Normal file
File diff suppressed because it is too large
Load Diff
374
lua/stormfox2/framework/cl_heavens.lua
Normal file
374
lua/stormfox2/framework/cl_heavens.lua
Normal file
@@ -0,0 +1,374 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- Fix overlapping tables
|
||||
StormFox2.Time = StormFox2.Time or {}
|
||||
local clamp,max,min = math.Clamp,math.max,math.min
|
||||
StormFox2.Sun = StormFox2.Sun or {}
|
||||
StormFox2.Moon = StormFox2.Moon or {}
|
||||
StormFox2.Sky = StormFox2.Sky or {}
|
||||
local sunVisible
|
||||
|
||||
-- Pipe Dawg
|
||||
|
||||
---Returns an obstruction-number between 0 - 1 fot the sun.
|
||||
---@return number sunVisible
|
||||
---@client
|
||||
function StormFox2.Sun.GetVisibility()
|
||||
return sunVisible or 1
|
||||
end
|
||||
-- Pixel are a bit crazy if called twice
|
||||
hook.Add("Think","StormFox2.Sun.PixUpdater",function()
|
||||
if not StormFox2.Loaded then return end
|
||||
if not _STORMFOX_SUNPIX then -- Create pixel
|
||||
_STORMFOX_SUNPIX = util.GetPixelVisibleHandle()
|
||||
else
|
||||
sunVisible = util.PixelVisible( LocalPlayer():GetPos() + StormFox2.Sun.GetAngle():Forward() * 4096, StormFox2.Mixer.Get("sun_size",30), _STORMFOX_SUNPIX )
|
||||
end
|
||||
end)
|
||||
|
||||
-- Sun overwrite
|
||||
SF_OLD_SUNINFO = SF_OLD_SUNINFO or util.GetSunInfo() -- Just in case
|
||||
|
||||
---Overrides util.GetSunInfo to use SF2 variables.
|
||||
---@ignore
|
||||
---@client
|
||||
---@return table GetSunInfo
|
||||
function util.GetSunInfo()
|
||||
if not StormFox2.Loaded or not sunVisible then -- In case we mess up
|
||||
if SF_OLD_SUNINFO then
|
||||
return SF_OLD_SUNINFO
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
local tab = {
|
||||
["direction"] = StormFox2.Sun.GetAngle():Forward(),
|
||||
["obstruction"] = sunVisible * (StormFox2.Mixer.Get("skyVisibility") / 100)}
|
||||
return tab
|
||||
end
|
||||
|
||||
-- SkyRender
|
||||
--[[-------------------------------------------------------------------------
|
||||
Render layers
|
||||
StarRender = Stars
|
||||
SunRender
|
||||
BlockStarRender (Will allow you to block out stars/sun)
|
||||
Moon
|
||||
CloudBox (Like a skybox, just with transparency. Will fade between states)
|
||||
CloudLayer (Moving clouds)
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Add("PostDraw2DSkyBox", "StormFox2.SkyBoxRender", function()
|
||||
if not StormFox2 then return end
|
||||
if not StormFox2.Loaded then return end
|
||||
-- Just to be sure. I hate errors in render hooks.
|
||||
if not StormFox2.util then return end
|
||||
if not StormFox2.Sun then return end
|
||||
if not StormFox2.Moon then return end
|
||||
if not StormFox2.Moon.GetAngle then return end
|
||||
if not StormFox2.Setting.SFEnabled() then return end
|
||||
local c_pos = StormFox2.util.RenderPos()
|
||||
local sky = StormFox2.Setting.GetCache("enable_skybox", true)
|
||||
local use_2d = StormFox2.Setting.GetCache("use_2dskybox",false)
|
||||
cam.Start3D( Vector( 0, 0, 0 ), EyeAngles() ,nil,nil,nil,nil,nil,1,32000) -- 2d maps fix
|
||||
render.OverrideDepthEnable( false,false )
|
||||
render.SuppressEngineLighting(true)
|
||||
render.SetLightingMode( 0 )
|
||||
if not use_2d or not sky then
|
||||
hook.Run("StormFox2.2DSkybox.StarRender", c_pos)
|
||||
-- hook.Run("StormFox2.2DSkybox.BlockStarRender",c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.SunRender", c_pos) -- No need to block, shrink the sun.
|
||||
|
||||
hook.Run("StormFox2.2DSkybox.Moon", c_pos)
|
||||
end
|
||||
hook.Run("StormFox2.2DSkybox.CloudBox", c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.CloudLayer", c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.PostCloudLayer",c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.FogLayer", c_pos)
|
||||
render.SuppressEngineLighting(false)
|
||||
render.SetLightingMode( 0 )
|
||||
render.OverrideDepthEnable( false, false )
|
||||
cam.End3D()
|
||||
|
||||
render.SetColorMaterial()
|
||||
end)
|
||||
|
||||
-- Render Sun
|
||||
local sunMat = Material("stormfox2/effects/sun/sun_mat")
|
||||
local sunMat2 = Material("stormfox2/effects/sun/sun_mat2")
|
||||
local sunMat3 = Material("stormfox2/effects/sun/sunflare")
|
||||
|
||||
local sunDot = 1
|
||||
hook.Add("StormFox2.2DSkybox.SunRender","StormFox2.RenderSun",function(c_pos)
|
||||
local SunA = StormFox2.Sun.GetAngle()
|
||||
local SunN = -SunA:Forward()
|
||||
|
||||
local sun = util.GetSunInfo()
|
||||
local viewAng = StormFox2.util.RenderAngles()
|
||||
-- Calculate dot
|
||||
local rawDot = ( SunA:Forward():Dot( viewAng:Forward() ) - 0.8 ) * 5
|
||||
if sun and sun.obstruction and sun.obstruction > 0 then
|
||||
sunDot = rawDot
|
||||
else
|
||||
sunDot = 0
|
||||
end
|
||||
-- Calculate close to edge
|
||||
local z = 1
|
||||
local p = math.abs(math.sin(math.rad(SunA.p))) -- How far we are away from sunset
|
||||
if p < 0.1 then
|
||||
z = 0.8 + p * 0.2
|
||||
end
|
||||
local s_size = StormFox2.Sun.GetSize() / 2
|
||||
local s_size2 = s_size * 1.2
|
||||
local s_size3 = s_size * 3 -- * math.max(0, rawDot)
|
||||
local c_c = StormFox2.Sun.GetColor() or color_white
|
||||
local c = Color(c_c.r,c_c.g,c_c.b,c_c.a)
|
||||
render.SetMaterial(sunMat)
|
||||
-- render.DrawQuadEasy( SunN * -200, SunN, s_size, s_size, c, 0 )
|
||||
render.SuppressEngineLighting(true)
|
||||
render.SetMaterial(sunMat2)
|
||||
render.DrawQuadEasy( SunN * -200, SunN, s_size2, s_size2, c, 0 )
|
||||
if sunDot > 0 then
|
||||
local a = (StormFox2.Mixer.Get("skyVisibility") / 100 - 0.5) * 2
|
||||
if a > 0 then
|
||||
c.a = a * 255
|
||||
render.SetMaterial(sunMat3)
|
||||
render.DrawQuadEasy( SunN * -200, SunN, s_size3 * sunDot , s_size3 * sunDot, c, 0 )
|
||||
end
|
||||
end
|
||||
render.SuppressEngineLighting(false)
|
||||
end)
|
||||
-- Sun and moon beams
|
||||
local beams = StormFox2.Setting.AddCL("enable_sunbeams", true)
|
||||
local matSunbeams = Material( "pp/sunbeams" )
|
||||
matSunbeams:SetTexture( "$fbtexture", render.GetScreenEffectTexture() )
|
||||
|
||||
local function SunRender( sunAltitude )
|
||||
if sunDot <= 0 then return false end
|
||||
-- Check if we see the sun at all
|
||||
local vis = StormFox2.Sun.GetVisibility()
|
||||
if ( vis == 0 ) then return false end
|
||||
|
||||
-- Brightness multiplier
|
||||
local bright
|
||||
if sunAltitude > 0 and sunAltitude < 30 then
|
||||
bright = 1
|
||||
elseif sunAltitude >= 30 then
|
||||
bright = 1.3 - 0.02 * sunAltitude
|
||||
else -- Under 0
|
||||
bright = 0.06 * sunAltitude + 1
|
||||
end
|
||||
if bright < 0 then return end -- Too far up in the sky
|
||||
local direciton = StormFox2.Sun.GetAngle():Forward()
|
||||
local beampos = EyePos() + direciton * 4096
|
||||
-- And screenpos
|
||||
local scrpos = beampos:ToScreen()
|
||||
local mul = vis * sunDot * bright
|
||||
if mul >= 0 then
|
||||
local s_size = StormFox2.Sun.GetSize()
|
||||
render.UpdateScreenEffectTexture()
|
||||
matSunbeams:SetFloat( "$darken", .96 )
|
||||
matSunbeams:SetFloat( "$multiply",0.7 * mul)
|
||||
matSunbeams:SetFloat( "$sunx", scrpos.x / ScrW() )
|
||||
matSunbeams:SetFloat( "$suny", scrpos.y / ScrH() )
|
||||
matSunbeams:SetFloat( "$sunsize", s_size / 850 )
|
||||
render.SetMaterial( matSunbeams )
|
||||
render.DrawScreenQuad()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function MoonRender( sunAltitude )
|
||||
-- Calculate brightness
|
||||
local skyVis = StormFox2.Mixer.Get("skyVisibility") / 100
|
||||
if skyVis <= 0 then return end
|
||||
-- Brightness of the moon
|
||||
local mP = StormFox2.Moon.GetPhase()
|
||||
if mP == SF_MOON_NEW then return end -- No moon
|
||||
-- Sun checkk
|
||||
local mul = 1
|
||||
if sunAltitude > -20 then
|
||||
mul = -0.2 * sunAltitude - 3
|
||||
end
|
||||
-- Phase multiplier (Full moon is 1, goes down to 0.25)
|
||||
local pmul = math.min(1, (-0.0714286 * mP^2 + 0.571429 * mP - 0.285714) * 1.17)
|
||||
local brightness = skyVis * mul * pmul
|
||||
if brightness <= 0 then return end
|
||||
local viewAng = StormFox2.util.RenderAngles()
|
||||
-- Calculate dot
|
||||
local moonAng = StormFox2.Moon.GetAngle()
|
||||
local rawDot = ( moonAng:Forward():Dot( viewAng:Forward() ) - 0.8 ) * 5
|
||||
brightness = brightness * rawDot
|
||||
if brightness <= 0 then return end
|
||||
|
||||
local direciton = StormFox2.Moon.GetAngle():Forward()
|
||||
local beampos = EyePos() + direciton * 4096
|
||||
-- And screenpos
|
||||
local scrpos = beampos:ToScreen()
|
||||
local s_size = StormFox2.Moon.GetSize()
|
||||
render.UpdateScreenEffectTexture()
|
||||
matSunbeams:SetFloat( "$darken", .5 )
|
||||
matSunbeams:SetFloat( "$multiply",0.15 * brightness)
|
||||
matSunbeams:SetFloat( "$sunx", scrpos.x / ScrW() )
|
||||
matSunbeams:SetFloat( "$suny", scrpos.y / ScrH() )
|
||||
matSunbeams:SetFloat( "$sunsize", s_size / 950 )
|
||||
render.SetMaterial( matSunbeams )
|
||||
render.DrawScreenQuad()
|
||||
end
|
||||
|
||||
hook.Add( "RenderScreenspaceEffects", "StormFox2.Sun.beams", function()
|
||||
if ( not render.SupportsPixelShaders_2_0() ) then return end
|
||||
if not StormFox2.Setting.SFEnabled() or not beams:GetValue() then return end
|
||||
local sunAltitude = StormFox2.Sun.GetAltitude()
|
||||
if sunAltitude > -15 then
|
||||
SunRender(sunAltitude)
|
||||
--else TODO: Looks kinda aweful sadly.
|
||||
--MoonRender(sunAltitude)
|
||||
end
|
||||
end )
|
||||
|
||||
-- Render moon
|
||||
-- Setup params and vars
|
||||
local CurrentMoonTexture = Material("stormfox2/effects/moon/rt_moon")
|
||||
local Mask_25 = Material("stormfox2/effects/moon/25.png")
|
||||
local Mask_0 = Material("stormfox2/effects/moon/0.png")
|
||||
local Mask_50 = Material("stormfox2/effects/moon/50.png")
|
||||
local Mask_75 = Material("stormfox2/effects/moon/75.png")
|
||||
local texscale = 512
|
||||
local RTMoonTexture = GetRenderTargetEx( "StormFox_RTMoon", texscale, texscale, 1, MATERIAL_RT_DEPTH_NONE, 2, CREATERENDERTARGETFLAGS_UNFILTERABLE_OK, IMAGE_FORMAT_RGBA8888)
|
||||
-- Functions to update the moon phase
|
||||
local lastRotation = -1
|
||||
local lastCurrentPhase = -1
|
||||
local lastMoonMat
|
||||
local function RenderMoonPhase(rotation,currentPhase)
|
||||
|
||||
--currentPhase = SF_MOON_FIRST_QUARTER - 0.01
|
||||
if currentPhase == SF_MOON_NEW then return end -- New moon. No need to render.
|
||||
-- Check if there is a need to re-render
|
||||
local moonMat = StormFox2.Mixer.Get("moonTexture",lastMoonMat)
|
||||
if type(moonMat) ~= "string" then return end -- Something went wrong. Lets wait.
|
||||
if lastCurrentPhase == currentPhase and lastMoonMat and lastMoonMat == moonMat then
|
||||
-- Already rendered
|
||||
return true
|
||||
end
|
||||
lastCurrentPhase = currentPhase
|
||||
lastMoonMat = moonMat
|
||||
moonMat = Material(moonMat)
|
||||
render.PushRenderTarget( RTMoonTexture )
|
||||
render.OverrideAlphaWriteEnable( true, true )
|
||||
|
||||
render.ClearDepth()
|
||||
render.Clear( 0, 0, 0, 0 )
|
||||
cam.Start2D()
|
||||
-- Render moon
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(moonMat)
|
||||
surface.DrawTexturedRectUV(0,0,texscale,texscale,-0.01,-0.01,1.01,1.01)
|
||||
-- Mask Start
|
||||
-- render.OverrideBlendFunc( true, BLEND_ZERO, BLEND_SRC_ALPHA, BLEND_DST_ALPHA, BLEND_ZERO )
|
||||
render.OverrideBlend(true, BLEND_ZERO, BLEND_SRC_ALPHA,0,BLEND_DST_ALPHA, BLEND_ZERO,0)
|
||||
-- Render mask
|
||||
surface.SetDrawColor(color_white)
|
||||
-- New to first q; 0 to 50%
|
||||
if currentPhase < SF_MOON_FIRST_QUARTER then
|
||||
local s = 7 - 3.5 * currentPhase
|
||||
surface.SetMaterial(Mask_25)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale * s,texscale,rotation)
|
||||
if currentPhase >= SF_MOON_WAXIN_CRESCENT then
|
||||
-- Ex step
|
||||
local x,y = math.cos(math.rad(-rotation)),math.sin(math.rad(-rotation))
|
||||
surface.SetMaterial(Mask_0)
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (-texscale * 0.51),texscale / 2 + y * (-texscale * 0.51),texscale * 1,texscale,rotation)
|
||||
end
|
||||
elseif currentPhase == SF_MOON_FIRST_QUARTER then -- 50%
|
||||
surface.SetMaterial(Mask_50)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale,texscale,rotation)
|
||||
elseif currentPhase < SF_MOON_FULL then -- 50% to 100%
|
||||
local s = (currentPhase - SF_MOON_FIRST_QUARTER) * 3
|
||||
surface.SetMaterial(Mask_75)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale * s,texscale,rotation + 180)
|
||||
local x,y = math.cos(math.rad(-rotation)),math.sin(math.rad(-rotation))
|
||||
surface.SetMaterial(Mask_0)
|
||||
if s < 0.2 then
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (-texscale * 0.5),texscale / 2 + y * (-texscale * 0.51),texscale * 1,texscale,rotation + 180)
|
||||
elseif s < 1 then
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (-texscale * 0.5),texscale / 2 + y * (-texscale * 0.51),texscale * 0.9,texscale,rotation + 180)
|
||||
end
|
||||
elseif currentPhase == SF_MOON_FULL then
|
||||
-- FULL MOON
|
||||
elseif currentPhase < SF_MOON_LAST_QUARTER then
|
||||
local s = 12 - (currentPhase - SF_MOON_FIRST_QUARTER) * 3
|
||||
surface.SetMaterial(Mask_75)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale * s,texscale,rotation)
|
||||
local x,y = math.cos(math.rad(-rotation)),math.sin(math.rad(-rotation))
|
||||
surface.SetMaterial(Mask_0)
|
||||
if s < 0.05 then
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (texscale * 0.5),texscale / 2 + y * (texscale * 0.51),texscale * 1,texscale,rotation)
|
||||
elseif s < 1 then
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (texscale * 0.5),texscale / 2 + y * (texscale * 0.51),texscale * 0.9,texscale,rotation)
|
||||
end
|
||||
elseif currentPhase == SF_MOON_LAST_QUARTER then
|
||||
surface.SetMaterial(Mask_50)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale,texscale,rotation + 180)
|
||||
elseif currentPhase < SF_MOON_WANING_CRESCENT + 1 then
|
||||
local s = (currentPhase - (SF_MOON_WANING_CRESCENT - 1)) * 3.5
|
||||
surface.SetMaterial(Mask_25)
|
||||
surface.DrawTexturedRectRotated(texscale / 2,texscale / 2,texscale * s,texscale,rotation + 180)
|
||||
if currentPhase >= SF_MOON_WAXIN_CRESCENT then
|
||||
-- Ex step
|
||||
local x,y = math.cos(math.rad(-rotation)),math.sin(math.rad(-rotation))
|
||||
surface.SetMaterial(Mask_0)
|
||||
surface.DrawTexturedRectRotated(texscale / 2 + x * (texscale * 0.51),texscale / 2 + y * (texscale * 0.51),texscale,texscale,rotation)
|
||||
end
|
||||
end
|
||||
-- Mask End
|
||||
render.OverrideBlend(false)
|
||||
render.OverrideAlphaWriteEnable( false )
|
||||
cam.End2D()
|
||||
render.OverrideAlphaWriteEnable( false )
|
||||
render.PopRenderTarget()
|
||||
CurrentMoonTexture:SetTexture("$basetexture",RTMoonTexture)
|
||||
end
|
||||
hook.Add("StormFox2.2DSkybox.Moon","StormFox2.RenderMoon",function(c_pos)
|
||||
local phase = StormFox2.Moon.GetPhase()
|
||||
if phase <= 0 then return end
|
||||
local moonScale = StormFox2.Mixer.Get("moonSize",20)
|
||||
local moonAng = StormFox2.Moon.GetAngle()
|
||||
local N = moonAng:Forward()
|
||||
local NN = -N
|
||||
local sa = moonAng.y
|
||||
-- Render texture
|
||||
-- currentYaw
|
||||
RenderMoonPhase( ((moonAng.p < 270 and moonAng.p > 90) and 180 or 0),phase)
|
||||
local c = StormFox2.Mixer.Get("moonColor",Color(170,170,170))
|
||||
local a = StormFox2.Mixer.Get("skyVisibility",100) * 2
|
||||
-- Dark moonarea
|
||||
-- PrintTable(CurrentMoonTexture:GetKeyValues())
|
||||
render.SetMaterial( CurrentMoonTexture )
|
||||
local aa = max(0,(3.125 * a) - 57.5)
|
||||
render.DrawQuadEasy( N * 200, NN, moonScale , moonScale, Color(c.r,c.g,c.b, aa ), sa )
|
||||
end)
|
||||
|
||||
if true then return end
|
||||
-- Render Sky
|
||||
local scale = 256 * 1.5
|
||||
local galixmat = Material("stormfox2/effects/nightsky3")
|
||||
local c = Color(255,255,255)
|
||||
hook.Add("StormFox2.2DSkybox.StarRender", "StormFox2.2DSkyBox.NS", function(c_pos)
|
||||
render.SetMaterial( galixmat )
|
||||
c.a = StormFox2.Mixer.Get("starFade",100) * 2.55
|
||||
c.a = 255
|
||||
local p = (0.001) * StormFox2.Time.GetSpeed_RAW()
|
||||
local ang = Angle((RealTime() * p) % 360,0,0)
|
||||
local n = ang:Forward() * 256
|
||||
-- render.DrawQuadEasy(n, -n, scale * 4, scale, c, (ang.p < 270 and ang.p > 90) and 30 or 30 + 180)
|
||||
-- render.DrawSphere(Vector(0,0,0), -10, 30, 30, c)
|
||||
|
||||
end)
|
||||
175
lua/stormfox2/framework/sh_date.lua
Normal file
175
lua/stormfox2/framework/sh_date.lua
Normal file
@@ -0,0 +1,175 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
Date
|
||||
|
||||
SV StormFox2.Date.SetYearDay( nDay ) Sets the yearday.
|
||||
SH StormFox2.Date.GetYearDay() Gets the yearday.
|
||||
SH StormFox2.Date.GetWeekDay( bNumbers ) Returns the weekday. Returns a number if bNumbers is true.
|
||||
SH StormFox2.Date.GetMonth( bNumbers ) Returns the month. Returns a number if bNumbers is true.
|
||||
SH StormFox2.Date.GetShortMonth() Returns the month in a 3-letter string.
|
||||
Sh StormFox2.Date.GetDay() Returns the day within the month.
|
||||
Sh StormFox2.Date.Get( bNumbers ) Returns the date in string format. MM/DD or DD/MM depending on location and settings. Returns in numbers if bNumbers is true.
|
||||
]]
|
||||
|
||||
StormFox2.Setting.AddSV("real_time",false)
|
||||
StormFox2.Date = {}
|
||||
|
||||
if SERVER then
|
||||
---Sets the yearday. [0-365]
|
||||
---@param nDay number
|
||||
---@server
|
||||
function StormFox2.Date.SetYearDay( nDay )
|
||||
StormFox2.Network.Set("day", nDay % 365)
|
||||
end
|
||||
end
|
||||
|
||||
---Returns the day within the year. [0 - 364]
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Date.GetYearDay()
|
||||
return StormFox2.Data.Get("day",0)
|
||||
end
|
||||
|
||||
local day, month, weekday = -1,-1,-1
|
||||
local function calcDate( nDay )
|
||||
local t = string.Explode("-", os.date( "%d-%m-%w", nDay * 86400 ), false)
|
||||
return tonumber(t[1]),tonumber(t[2]),tonumber(t[3])
|
||||
end
|
||||
hook.Add("StormFox2.data.change", "StormFox2.date.update", function(sKey, nDay)
|
||||
if sKey ~= "day" then return end
|
||||
day,month,weekday = calcDate( nDay )
|
||||
end)
|
||||
do
|
||||
local t = {
|
||||
[0] = "Sunday",
|
||||
[1] = "Monday",
|
||||
[2] = "Tuesday",
|
||||
[3] = "Wednesday",
|
||||
[4] = "Thursday",
|
||||
[5] = "Friday",
|
||||
[6] = "Saturday"
|
||||
}
|
||||
|
||||
---Returns the current weekday ["Monday" - "Sunday"]. Does also accept a number between 0 - 6.
|
||||
---@param number nil|number
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Date.GetWeekDay( number )
|
||||
if type(number) == "number" then -- FFS people
|
||||
return t[ number % 7 ] or "Unknown"
|
||||
end
|
||||
return t[ weekday ] or "Unknown"
|
||||
end
|
||||
end
|
||||
do
|
||||
local t = {
|
||||
[1] = "January",
|
||||
[2] = "February",
|
||||
[3] = "March",
|
||||
[4] = "April",
|
||||
[5] = "May",
|
||||
[6] = "June",
|
||||
[7] = "July",
|
||||
[8] = "August",
|
||||
[9] = "September",
|
||||
[10] = "October",
|
||||
[11] = "November",
|
||||
[12] = "December"
|
||||
}
|
||||
---Returns the current month ["January" - "December"]. Also accepts a number between 1 - 12.
|
||||
---@param number nil|number
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Date.GetMonth( number )
|
||||
if type(number) == "number" then -- FFS people
|
||||
return t[ number % 13 ] or "Unknown"
|
||||
end
|
||||
return t[ month ] or "Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
---Returns the current month in short ["Jan" - "Dec"]. Also accepts a number between 1 - 12.
|
||||
---@param number nil|number
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Date.GetShortMonth( number )
|
||||
return string.sub(StormFox2.Date.GetMonth( number ),0,3)
|
||||
end
|
||||
|
||||
--- Returns the day of the month: 1 - 31.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Date.GetDay()
|
||||
return day
|
||||
end
|
||||
|
||||
local country = system.GetCountry() or "UK"
|
||||
local crazy_countries = {"AS", "BT", "CN", "FM", "GU", "HU", "JP", "KP", "KR", "LT", "MH", "MN", "MP", "TW", "UM", "US", "VI"}
|
||||
local default = table.HasValue(crazy_countries, country)
|
||||
if CLIENT then
|
||||
StormFox2.Setting.AddCL("use_monthday",default,"Display MM/DD instead of DD/MM.")
|
||||
end
|
||||
|
||||
local tOrdinal = {"st", "nd", "rd"}
|
||||
local function ordinal(n)
|
||||
local digit = tonumber(string.sub(n, -1))
|
||||
local two_dig = tonumber(string.sub(n,-2))
|
||||
if digit > 0 and digit <= 3 and two_dig ~= 11 and two_dig ~= 12 and two_dig ~= 13 then
|
||||
return n .. tOrdinal[digit]
|
||||
else
|
||||
return n .. "th"
|
||||
end
|
||||
end
|
||||
|
||||
---Returns the current date-format: "6/11/22". Based on systems country location or clients setting.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Date.Get( )
|
||||
local m = StormFox2.Date.GetMonth( )
|
||||
local d = StormFox2.Date.GetDay()
|
||||
if bNumbers and m < 10 then
|
||||
m = "0" .. m
|
||||
elseif not bNumbers then
|
||||
d = ordinal(d)
|
||||
end
|
||||
local rev
|
||||
if CLIENT then
|
||||
rev = StormFox2.Setting.GetCache("use_monthday",default)
|
||||
else
|
||||
rev = default
|
||||
end
|
||||
local e = bNumbers and " / " or " "
|
||||
|
||||
if not rev then
|
||||
return d .. e .. m
|
||||
else
|
||||
return m .. e .. d
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
-- Sets the starting day.
|
||||
if StormFox2.Setting.Get("real_time", false) then
|
||||
StormFox2.Network.Set("day", tonumber(os.date("%j")))
|
||||
else
|
||||
StormFox2.Network.Set("day", cookie.GetNumber("sf_date", math.random(0,364)))
|
||||
end
|
||||
-- Saves the day for next start.
|
||||
hook.Add("ShutDown","StormFox2.Day.Save",function()
|
||||
cookie.Set("sf_date",StormFox2.Date.GetYearDay())
|
||||
end)
|
||||
-- Sets the day to the current day, if real_time gets switched on.
|
||||
StormFox2.Setting.Callback("real_time",function(switch)
|
||||
if not switch then return end
|
||||
StormFox2.Network.Set("day", os.date("%j"))
|
||||
end,"sf_convar_data")
|
||||
end
|
||||
89
lua/stormfox2/framework/sh_defaultgamemodes.lua
Normal file
89
lua/stormfox2/framework/sh_defaultgamemodes.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
| 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("random_round_weather",true,nil,"Weather")
|
||||
|
||||
local gamemodes = {"terrortown"}
|
||||
local isRGame = table.HasValue(gamemodes, engine.ActiveGamemode())
|
||||
|
||||
local nightBlock = false
|
||||
|
||||
local function SelectRandom()
|
||||
-- Temp
|
||||
local tmin,tmax = StormFox2.Setting.Get("min_temp",-10), StormFox2.Setting.Get("max_temp",20)
|
||||
StormFox2.Temperature.Set( math.random(tmin, tmax) )
|
||||
-- Wind
|
||||
StormFox2.Wind.SetForce( math.random(1, 20))
|
||||
StormFox2.Wind.SetYaw( math.random(360))
|
||||
-- Select random weather
|
||||
local w_name
|
||||
local w_p = math.Rand(0.4, 0.9)
|
||||
if math.random(0,10) > 5 then
|
||||
w_name = table.Random(StormFox2.Weather.GetAllSpawnable())
|
||||
elseif math.random(1, 2) > 1 then
|
||||
w_name = "Cloud"
|
||||
else
|
||||
w_name = "Clear"
|
||||
end
|
||||
local w_t = StormFox2.Weather.Get(w_name)
|
||||
if w_t.thunder and w_t.thunder(w_p) then
|
||||
StormFox2.Thunder.SetEnabled( true, w_t.thunder(w_p), math.random(1,3) * 60 )
|
||||
else
|
||||
StormFox2.Thunder.SetEnabled( false )
|
||||
end
|
||||
-- Set random time
|
||||
local start = StormFox2.Setting.Get("start_time",-1) or -1
|
||||
if start < 0 then
|
||||
if nightBlock then
|
||||
StormFox2.Time.Set( math.random(500, 900 ) )
|
||||
w_p = math.Rand(0.4, 0.75) -- Reroll
|
||||
else
|
||||
StormFox2.Time.Set( math.random(60, 1080) )
|
||||
end
|
||||
end
|
||||
StormFox2.Weather.Set( w_name, w_p )
|
||||
end
|
||||
|
||||
hook.Add("StormFox2.Settings.PGL", "StormFox2.DefaultGamemodeSettings", function()
|
||||
local GM = gmod.GetGamemode()
|
||||
if not StormFox2.Setting.Get("random_round_weather", true) then return end
|
||||
if not isRGame and not GM.OnPreRoundStart then return end
|
||||
if not GM.SF2_Settings then
|
||||
GM.SF2_Settings = {
|
||||
["auto_weather"] = 0,
|
||||
["hide_forecast"] = 1,
|
||||
["openweathermap_enabled"] = 0,
|
||||
["time_speed"] = 1,
|
||||
["maplight_auto"] = 1
|
||||
}
|
||||
-- These gamemodes are quick-roundbased. 2~6 mins or so. Block the exspensive light-changes.
|
||||
if not StormFox2.Ent.light_environments then
|
||||
GM.SF2_Settings["allow_weather_lightchange"] = 0
|
||||
nightBlock = true
|
||||
end
|
||||
end
|
||||
if GM.PreRoundStart then
|
||||
_SFGMPRERS = _SFGMPRERS or GM.PreRoundStart
|
||||
function GM.PreRoundStart( ... )
|
||||
_SFGMPRERS( ... )
|
||||
if not StormFox2.Setting.Get("random_round_weather") then return end
|
||||
SelectRandom()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Random TTT round
|
||||
if SERVER then
|
||||
hook.Add("TTTPrepareRound", "StormFox2.TTT", function()
|
||||
if not StormFox2.Setting.Get("random_round_weather") then return end
|
||||
SelectRandom()
|
||||
end)
|
||||
end
|
||||
333
lua/stormfox2/framework/sh_heavens.lua
Normal file
333
lua/stormfox2/framework/sh_heavens.lua
Normal file
@@ -0,0 +1,333 @@
|
||||
--[[
|
||||
| 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.Sun.SetTimeUp(nTime) Sets how long the sun is on the sky.
|
||||
StormFox2.Sun.IsUp() Returns true if the sun is on the sky.
|
||||
|
||||
|
||||
StormFox2.Moon.SetTimeUp(nTime) Sets how long the moon is on the sky.
|
||||
|
||||
---------------------------------------------------------------------------]]
|
||||
local clamp = math.Clamp
|
||||
|
||||
StormFox2.Sun = StormFox2.Sun or {}
|
||||
StormFox2.Moon = StormFox2.Moon or {}
|
||||
StormFox2.Sky = StormFox2.Sky or {}
|
||||
|
||||
-- SF_SKY_DAY = 0
|
||||
-- SF_SKY_SUNRISE = 1
|
||||
-- SF_SKY_SUNSET = 2
|
||||
-- SF_SKY_CEVIL = 3
|
||||
-- SF_SKY_BLUE_HOUR = 4
|
||||
-- SF_SKY_NAUTICAL = 5
|
||||
-- SF_SKY_ASTRONOMICAL = 6
|
||||
-- SF_SKY_NIGHT = 7
|
||||
|
||||
StormFox2.Setting.AddSV("sunyaw",88,nil, "Effects", 0, 360)
|
||||
StormFox2.Setting.AddSV("moonlock",true,nil,"Effects")
|
||||
local phase = StormFox2.Setting.AddSV("moonphase",true,nil,"Effects")
|
||||
|
||||
StormFox2.Setting.AddSV("enable_skybox",true,nil, "Effect")
|
||||
StormFox2.Setting.AddSV("use_2dskybox",false,nil, "Effects")
|
||||
StormFox2.Setting.AddSV("overwrite_2dskybox","",nil, "Effects")
|
||||
|
||||
if CLIENT then -- From another file
|
||||
StormFox2.Setting.AddSV("darken_2dskybox", false, nil, "Effect")
|
||||
end
|
||||
|
||||
-- Sun and Sun functions
|
||||
---Returns the time when the sun rises.
|
||||
---@return TimeNumber
|
||||
---@shared
|
||||
function StormFox2.Sun.GetSunRise()
|
||||
return StormFox2.Setting.Get("sunrise")
|
||||
end
|
||||
|
||||
---Returns the time when the sun sets.
|
||||
---@return TimeNumber
|
||||
---@shared
|
||||
function StormFox2.Sun.GetSunSet()
|
||||
return StormFox2.Setting.Get("sunset")
|
||||
end
|
||||
|
||||
---Returns the time when sun is at its higest.
|
||||
---@return TimeNumber
|
||||
---@shared
|
||||
function StormFox2.Sun.GetSunAtHigest()
|
||||
return (StormFox2.Sun.GetSunRise() + StormFox2.Sun.GetSunSet()) / 2
|
||||
end
|
||||
|
||||
---Returns the sun and moon-yaw. (Normal 90)
|
||||
---@return number yaw
|
||||
function StormFox2.Sun.GetYaw()
|
||||
return StormFox2.Setting.Get("sunyaw")
|
||||
end
|
||||
|
||||
---Returns true if the sun is up
|
||||
---@param nTime? TimeNumber
|
||||
---@return boolean
|
||||
function StormFox2.Sun.IsUp(nTime)
|
||||
return StormFox2.Time.IsBetween(StormFox2.Sun.GetSunRise(), StormFox2.Sun.GetSunSet(),nTime)
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the sun-size. (Normal 30)
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---Returns the sunsize.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Sun.GetSize()
|
||||
return StormFox2.Mixer.Get("sun_size",30) or 30
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the sun-color.
|
||||
---------------------------------------------------------------------------]]
|
||||
function StormFox2.Sun.GetColor()
|
||||
return StormFox2.Mixer.Get("sunColor",Color(255,255,255))
|
||||
end
|
||||
local sunVisible = 0
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the sunangle for the current or given time.
|
||||
---------------------------------------------------------------------------]]
|
||||
local function GetSunPitch()
|
||||
local p = StormFox2.Time.GetCycleTime() * 360
|
||||
return p
|
||||
end
|
||||
function StormFox2.Sun.GetAngle()
|
||||
local a = Angle(-GetSunPitch(),StormFox2.Sun.GetYaw(),0)
|
||||
return a
|
||||
end
|
||||
-- Returns the sun altitude. 0 degree at sunrise/set and 90 degrees at noon.
|
||||
function StormFox2.Sun.GetAltitude()
|
||||
local a = GetSunPitch(nTime)
|
||||
if a > 90 and a < 270 then
|
||||
return 180 - a
|
||||
elseif a > 270 then
|
||||
return -(360 - a)
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
-- We need a sun-stamp. We can't go by time.
|
||||
local sunOffset = 5 -- Sunset needs to be pushed
|
||||
local stamp = {
|
||||
[0] = {SF_SKY_SUNRISE,6,"SunRise"}, -- 6
|
||||
[6] = {SF_SKY_DAY, 168,"Day"}, -- 180 - 6
|
||||
[174 + sunOffset] = {SF_SKY_SUNSET,6,"SunSet"}, -- 174 + 6
|
||||
[180 + sunOffset] = {SF_SKY_CEVIL,4,"Cevil"}, -- 4
|
||||
[184 + sunOffset] = {SF_SKY_BLUE_HOUR,2,"Blue Hour"}, -- 6
|
||||
[186 + sunOffset] = {SF_SKY_NAUTICAL,6,"Nautical"}, -- 12
|
||||
[192 + sunOffset] = {SF_SKY_ASTRONOMICAL,6,"Astronomical"}, -- 18
|
||||
[198 + sunOffset] = {SF_SKY_NIGHT,168,"Night"}, -- 144
|
||||
[342] = {SF_SKY_ASTRONOMICAL,6,"Astronomical"}, -- 18
|
||||
[348] = {SF_SKY_NAUTICAL,6,"Nautical"}, -- 12
|
||||
[354] = {SF_SKY_BLUE_HOUR,2,"Blue Hour"}, -- 6
|
||||
[356] = {SF_SKY_CEVIL,4,"Cevil"}, -- 4
|
||||
[360] = {SF_SKY_SUNRISE,6,"SunRise"},
|
||||
[370] = {SF_SKY_SUNRISE,6,"SunRise"}, -- 6
|
||||
}
|
||||
-- Make an array of keys
|
||||
local stamp_arr = table.GetKeys(stamp)
|
||||
table.sort(stamp_arr, function(a,b) return a < b end)
|
||||
-- Fix calculating second argument
|
||||
for id, pitch in pairs(stamp_arr) do
|
||||
local n_pitch = stamp_arr[id + 1] or stamp_arr[1]
|
||||
local ad = math.AngleDifference(n_pitch, pitch)
|
||||
if ad == 0 then
|
||||
ad = stamp[n_pitch][2]
|
||||
end
|
||||
stamp[pitch][2] = ad
|
||||
end
|
||||
-- Calculate the sunsize
|
||||
local lC,lCV = -1,-1
|
||||
local function GetsunSize()
|
||||
if lC > CurTime() then return lCV end
|
||||
lC = CurTime() + 2
|
||||
local x = StormFox2.Sun.GetSize() or 20
|
||||
lCV = (-0.00019702 * x^2 + 0.149631 * x - 0.0429803) / 2
|
||||
return lCV
|
||||
end
|
||||
-- Returns: Stamp-ptch, Sun-pitch, Stamp-pitch
|
||||
local function GetStamp(nTime,nOffsetDegree)
|
||||
local sunSize = GetsunSize()
|
||||
local p = ( GetSunPitch(nTime) + (nOffsetDegree or 0) ) % 360
|
||||
-- Offset the sunsize
|
||||
if p > 90 and p < 270 then -- Sunrise
|
||||
p = (p - sunSize) % 360
|
||||
else -- Sunset
|
||||
p = (p + sunSize) % 360
|
||||
end
|
||||
-- Locate the sunstamp by angle
|
||||
local c_pitch, id = -1
|
||||
for n, pitch in pairs(stamp_arr) do
|
||||
if p >= pitch and c_pitch < pitch then
|
||||
id = n
|
||||
c_pitch = pitch
|
||||
end
|
||||
end
|
||||
return stamp_arr[id], p, stamp_arr[id + 1] or stamp_arr[1]
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the sun-stamp.
|
||||
First argument:
|
||||
0 = day, 1 = golden hour, 2 = cevil, 3 = blue hour
|
||||
4 = nautical, 5 = astronomical, 6 = night
|
||||
|
||||
Second argument:
|
||||
Pitch
|
||||
|
||||
Second argument
|
||||
Next stamp
|
||||
0 = day, 1 = golden hour, 2 = cevil, 3 = blue hour
|
||||
4 = nautical, 5 = astronomical, 6 = night
|
||||
---------------------------------------------------------------------------]]
|
||||
local function GetStamp(nTime,nOffsetDegree)
|
||||
local sunSize = GetsunSize()
|
||||
local p = ( GetSunPitch(nTime) + (nOffsetDegree or 0) ) % 360
|
||||
-- Offset the sunsize
|
||||
if p > 90 and p < 270 then -- Sunrise
|
||||
p = (p - sunSize) % 360
|
||||
else -- Sunset
|
||||
p = (p + sunSize) % 360
|
||||
end
|
||||
-- Locate the sunstamp by angle
|
||||
local c_pitch, id = -1
|
||||
for n, pitch in pairs(stamp_arr) do
|
||||
if p >= pitch and c_pitch < pitch then
|
||||
id = n
|
||||
c_pitch = pitch
|
||||
end
|
||||
end
|
||||
if not id then
|
||||
return SF_SKY_DAY, p, SF_SKY_CEVIL
|
||||
end
|
||||
return stamp_arr[id],p,stamp_arr[id + 1] or stamp_arr[1]
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the sun-stamp.
|
||||
First argument:
|
||||
0 = day, 1 = golden hour, 2 = cevil, 3 = blue hour
|
||||
4 = nautical, 5 = astronomical, 6 = night
|
||||
|
||||
Second argument:
|
||||
Percent used of the current stamp
|
||||
|
||||
Third argument
|
||||
Next stamp
|
||||
0 = day, 1 = golden hour, 2 = cevil, 3 = blue hour
|
||||
4 = nautical, 5 = astronomical, 6 = night
|
||||
|
||||
Forth argument
|
||||
Stamps pitch length
|
||||
---------------------------------------------------------------------------]]
|
||||
local nP = 0
|
||||
function StormFox2.Sky.GetStamp(nTime,nOffsetDegree)
|
||||
local c_stamp,p,n_stamp = GetStamp(nTime,nOffsetDegree) -- p is current angle
|
||||
local per = (p - c_stamp) / (n_stamp - c_stamp)
|
||||
return stamp[c_stamp][1], per, stamp[n_stamp][1],stamp[c_stamp][2] -- 1 = Stamp, 2 = Type of stamp
|
||||
end
|
||||
-- Returns the last stamp
|
||||
local lastStamp = 0
|
||||
function StormFox2.Sky.GetLastStamp()
|
||||
return lastStamp
|
||||
end
|
||||
-- Sky hook. Used to update the sky colors and other things.
|
||||
local nextStamp = -1
|
||||
hook.Add("StormFox2.Time.Changed","StormFox2.Sky.UpdateStamp",function()
|
||||
nextStamp = -1
|
||||
end)
|
||||
timer.Create("StormFox2.Sky.Stamp", 1, 0, function()
|
||||
--local c_t = CurTime()
|
||||
--if c_t < nextStamp then return end
|
||||
local stamp,n_t = StormFox2.Sky.GetStamp(nil,6) -- Look 6 degrees into the furture so we can lerp the colors.
|
||||
--nextStamp = c_t + (n_t * SunDelta) / StormFox2.Time.GetSpeed()
|
||||
--[[-------------------------------------------------------------------------
|
||||
This hook gets called when the sky-stamp changes. This is used to change the sky-colors and other things.
|
||||
First argument:
|
||||
0 = day, 1 = golden hour, 2 = cevil, 3 = blue hour
|
||||
4 = nautical, 5 = astronomical, 6 = night
|
||||
|
||||
Second argument:
|
||||
The lerp-time to change the variable.
|
||||
---------------------------------------------------------------------------]]
|
||||
if lastStamp == stamp then return end -- Don't call it twice.
|
||||
lastStamp = stamp
|
||||
local delta = 180 / (StormFox2.Setting.Get("sunset") - StormFox2.Setting.Get("sunrise"))
|
||||
hook.Run("StormFox2.Sky.StampChange", stamp, 6 / math.max(1, delta) )
|
||||
end)
|
||||
-- Moon and its functions
|
||||
--[[-------------------------------------------------------------------------
|
||||
Moon phases
|
||||
---------------------------------------------------------------------------]]
|
||||
SF_MOON_NEW = 0
|
||||
SF_MOON_WAXIN_CRESCENT = 1
|
||||
SF_MOON_FIRST_QUARTER = 2
|
||||
SF_MOON_WAXING_GIBBOUS = 3
|
||||
SF_MOON_FULL = 4
|
||||
SF_MOON_WANING_GIBBOUS = 5
|
||||
SF_MOON_LAST_QUARTER = 6
|
||||
SF_MOON_WANING_CRESCENT = 7
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the moon phase for the current day
|
||||
---------------------------------------------------------------------------]]
|
||||
function StormFox2.Moon.GetPhase()
|
||||
if not phase:GetValue() then return SF_MOON_FULL end
|
||||
return StormFox2.Data.Get("moon_phase",SF_MOON_FULL)
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the moon phase name
|
||||
---------------------------------------------------------------------------]]
|
||||
function StormFox2.Moon.GetPhaseName(nTime)
|
||||
local n = StormFox2.Moon.GetPhase(nTime)
|
||||
if n == SF_MOON_NEW then return "New Moon" end
|
||||
if n == SF_MOON_WAXIN_CRESCENT then return "Waxin Crescent" end
|
||||
if n == SF_MOON_FIRST_QUARTER then return "First Quarter" end
|
||||
if n == SF_MOON_WAXING_GIBBOUS then return "Waxing Gibbous" end
|
||||
if n == SF_MOON_FULL then return "Full Moon" end
|
||||
if n == SF_MOON_WANING_GIBBOUS then return "Waning Gibbous" end
|
||||
if n == SF_MOON_LAST_QUARTER then return "Last Quarter" end
|
||||
if n == SF_MOON_WANING_CRESCENT then return "Waning Crescent" end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the angle for the moon. First argument can also be a certain time.
|
||||
---------------------------------------------------------------------------]]
|
||||
local tf = 0
|
||||
local a = 7 / 7.4
|
||||
function StormFox2.Moon.GetAngle(nTime)
|
||||
local p = 180 + StormFox2.Time.GetCycleTime() * 360
|
||||
if StormFox2.Setting.Get("moonlock",false) then
|
||||
return Angle(-p % 360, StormFox2.Sun.GetYaw(),0)
|
||||
end
|
||||
--if true then return Angle(200,StormFox2.Data.Get("sun_yaw",90),0) end
|
||||
local rDay = StormFox2.Date.GetYearDay()
|
||||
p = p + ( StormFox2.Moon.GetPhase() - 4 ) * 45
|
||||
return Angle(-p % 360,StormFox2.Sun.GetYaw(),0)
|
||||
end
|
||||
-- It might take a bit for the server to tell us the day changed.
|
||||
hook.Add("StormFox2.data.change", "StormFox2.moon.datefix", function(sKey, nDay)
|
||||
if sKey ~= "day" then return end
|
||||
tf = 0
|
||||
end)
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns true if the moon is up.
|
||||
---------------------------------------------------------------------------]]
|
||||
function StormFox2.Moon.IsUp()
|
||||
local t = StormFox2.Moon.GetAngle().p
|
||||
local s = StormFox2.Mixer.Get("moonSize",20) / 6.9
|
||||
return t > 180 - s or t < s
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Returns the moon size
|
||||
---------------------------------------------------------------------------]]
|
||||
function StormFox2.Moon.GetSize()
|
||||
return StormFox2.Mixer.Get("moonSize",20)
|
||||
end
|
||||
788
lua/stormfox2/framework/sh_maplight.lua
Normal file
788
lua/stormfox2/framework/sh_maplight.lua
Normal file
@@ -0,0 +1,788 @@
|
||||
--[[
|
||||
| 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("maplight_min",0,mil, "Effects", 0, 100)
|
||||
StormFox2.Setting.AddSV("maplight_max",80,nil, "Effects", 0, 100)
|
||||
StormFox2.Setting.AddSV("maplight_smooth",true,nil, "Effects",0,1)
|
||||
|
||||
StormFox2.Setting.AddSV("maplight_auto", true, nil, "Effects")
|
||||
StormFox2.Setting.AddSV("maplight_lightenv", false,nil, "Effects")
|
||||
StormFox2.Setting.AddSV("maplight_colormod", false,nil, "Effects")
|
||||
StormFox2.Setting.AddSV("maplight_dynamic", false,nil, "Effects")
|
||||
StormFox2.Setting.AddSV("maplight_lightstyle", false,nil, "Effects")
|
||||
|
||||
--[[----------------------------------------------------------------]]--
|
||||
StormFox2.Setting.AddSV("maplight_updaterate",game.SinglePlayer() and 6 or 3,nil, "Effects")
|
||||
|
||||
|
||||
StormFox2.Setting.AddSV("overwrite_extra_darkness",-1,nil, "Effects", -1, 1)
|
||||
StormFox2.Setting.SetType( "overwrite_extra_darkness", "special_float")
|
||||
|
||||
StormFox2.Setting.AddSV("allow_weather_lightchange",true,nil, "Weather")
|
||||
|
||||
if CLIENT then
|
||||
StormFox2.Setting.AddCL("extra_darkness",render.SupportsPixelShaders_2_0(),nil,"Effects",0,1)
|
||||
StormFox2.Setting.AddCL("extra_darkness_amount",0.75,nil, "Effects",0,1)
|
||||
StormFox2.Setting.SetType( "extra_darkness_amount", "float" )
|
||||
end
|
||||
|
||||
-- Converts a lightlvl to char
|
||||
local function convertToBZ( nNum ) -- From b to z
|
||||
local byte = math.Round(6 * nNum / 25 + 98)
|
||||
return string.char(byte)
|
||||
end
|
||||
local function convertToAZ( nNum )
|
||||
return string.char(97 + nNum / 4)
|
||||
end
|
||||
|
||||
local SetLightStyle, SetLightEnv
|
||||
if SERVER then
|
||||
-- Sets the lightstyle
|
||||
local function _SetLightStyle( char )
|
||||
engine.LightStyle(0,char)
|
||||
net.Start(StormFox2.Net.LightStyle)
|
||||
net.WriteUInt(string.byte(char), 7)
|
||||
net.Broadcast()
|
||||
end
|
||||
local var = 'm'
|
||||
-- Making it a timer, gives other scripts time to overwrite it.
|
||||
SetLightStyle = function( char )
|
||||
if char == 'a' then char = 'b' end -- 'a' will break all light on the map
|
||||
if char == var then return end
|
||||
var = char
|
||||
if timer.Exists("sf_lightstyleset") then return end
|
||||
timer.Create( "sf_lightstyleset", 1, 1, function()
|
||||
_SetLightStyle( var )
|
||||
end)
|
||||
end
|
||||
local oldLight = 'm'
|
||||
local has_faded = false
|
||||
SetLightEnv = function( char)
|
||||
if not StormFox2.Ent.light_environments then return end
|
||||
if char == oldLight then return end
|
||||
oldLight = char
|
||||
for _,light in ipairs(StormFox2.Ent.light_environments) do -- Doesn't lag
|
||||
if not IsValid(light) then continue end
|
||||
if has_faded then
|
||||
light:Fire("FadeToPattern", char ,0)
|
||||
else
|
||||
light:Fire("SetPattern", char ,0)
|
||||
end
|
||||
if char == "a" then
|
||||
light:Fire("TurnOff","",0)
|
||||
else
|
||||
light:Fire("TurnOn","",0)
|
||||
end
|
||||
light:Activate()
|
||||
end
|
||||
has_faded = true
|
||||
end
|
||||
else
|
||||
local last_sv
|
||||
net.Receive(StormFox2.Net.LightStyle, function(len)
|
||||
local c_var = net.ReadUInt(7)
|
||||
if last_sv and last_sv == c_var then return end -- No need
|
||||
last_sv = c_var
|
||||
timer.Simple(1, function()
|
||||
render.RedownloadAllLightmaps( true, true )
|
||||
--MsgC(color_white,"Redownload ligthmap [" .. last_sv .. "]\n")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
--[[ Diffrent types of maplight options.
|
||||
available = Return true to indicate this option is available. No function = always.
|
||||
on = When you switch it on
|
||||
off = When you switch it off
|
||||
change = When the lightvl changes. Secondary is when the smoothness ends.
|
||||
]]
|
||||
|
||||
local mapLights = {}
|
||||
local e_light_env = 0
|
||||
local e_lightstyle = 1
|
||||
local e_colormod = 2
|
||||
local e_lightdynamic = 3
|
||||
|
||||
local lastSetting = {}
|
||||
-- light_environment (SV) Fast, but not all maps have it
|
||||
mapLights[e_light_env] = {}
|
||||
mapLights[e_light_env]["available"] = function()
|
||||
return StormFox2.Ent.light_environments and true or false
|
||||
end
|
||||
if SERVER then
|
||||
mapLights[e_light_env]["on"] = function(lightLvl)
|
||||
SetLightEnv( convertToAZ(lightLvl) )
|
||||
end
|
||||
mapLights[e_light_env]["off"] = function(lightLvl)
|
||||
if lastSetting[e_lightdynamic] then
|
||||
SetLightEnv('b')
|
||||
else
|
||||
SetLightEnv('m')
|
||||
end
|
||||
end
|
||||
mapLights[e_light_env]["change"] = mapLights[e_light_env]["on"]
|
||||
end
|
||||
|
||||
-- light_style (SV) Laggy on large maps
|
||||
mapLights[e_lightstyle] = {}
|
||||
if SERVER then
|
||||
mapLights[e_lightstyle]["on"] = function(lightLvl)
|
||||
SetLightStyle( convertToBZ(lightLvl) )
|
||||
end
|
||||
mapLights[e_lightstyle]["off"] = function(lightLvl)
|
||||
SetLightStyle('m')
|
||||
timer.Remove( "sf_lightstyle" )
|
||||
end
|
||||
local nextSet
|
||||
mapLights[e_lightstyle]["change"] = function(lightLvl, full) -- We make this a 30sec timer, since lightstyle is so slow and laggy.
|
||||
if not full then return end -- Ignore 'smoothness' light
|
||||
if timer.Exists("sf_lightstyle") then
|
||||
nextSet = convertToBZ(lightLvl)
|
||||
else
|
||||
SetLightStyle(convertToBZ(lightLvl))
|
||||
timer.Create("sf_lightstyle", 5, 1, function()
|
||||
if not nextSet then return end
|
||||
SetLightStyle(nextSet)
|
||||
nextSet = nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ColorMod (CL) A fast alternative
|
||||
mapLights[e_colormod] = {}
|
||||
local cmod_on
|
||||
if CLIENT then
|
||||
local fardetarget = 0
|
||||
mapLights[e_colormod]["on"] = function(lightLvl)
|
||||
cmod_on = (1 - (lightLvl / 80)) * 0.7
|
||||
fardetarget = 0
|
||||
end
|
||||
mapLights[e_colormod]["off"] = function(lightLvl)
|
||||
cmod_on = nil
|
||||
end
|
||||
mapLights[e_colormod]["change"] = function(lightLvl)
|
||||
cmod_on = (1 - (lightLvl / 80)) * 0.7
|
||||
end
|
||||
local tab = {
|
||||
[ "$pp_colour_addr" ] = -0.09,
|
||||
[ "$pp_colour_addg" ] = -0.1,
|
||||
[ "$pp_colour_addb" ] = -0.05,
|
||||
[ "$pp_colour_brightness" ] = 0,
|
||||
[ "$pp_colour_contrast" ] = 1,
|
||||
[ "$pp_colour_colour" ] = 1,
|
||||
[ "$pp_colour_mulr" ] = 0,
|
||||
[ "$pp_colour_mulg" ] = 0,
|
||||
[ "$pp_colour_mulb" ] = 0
|
||||
}
|
||||
|
||||
hook.Add( "RenderScreenspaceEffects", "StormFox2.MapLightCMod", function()
|
||||
if not cmod_on then return end
|
||||
local darkness = cmod_on
|
||||
local env = StormFox2.Environment.Get()
|
||||
if not env.outside then
|
||||
if not env.nearest_outside then
|
||||
darkness = 0
|
||||
else
|
||||
local dis = 1 - ( env.nearest_outside:DistToSqr(StormFox2.util.RenderPos() or EyePos()) / 90000 )
|
||||
dis = math.Clamp(dis, 0, 1)
|
||||
darkness = darkness * 0.2 + darkness * 0.8 * dis
|
||||
end
|
||||
end
|
||||
fardetarget = math.Approach(fardetarget, darkness, FrameTime() * 0.5)
|
||||
local r_var = fardetarget
|
||||
local tL = math.min(255,StormFox2.Thunder.GetLight() or 0) / 255
|
||||
if tL > 0 then
|
||||
r_var = r_var * (1 - tL)
|
||||
end
|
||||
tab[ "$pp_colour_addr" ] = -0.09 * r_var
|
||||
tab[ "$pp_colour_addg" ] = -0.1 * r_var
|
||||
tab[ "$pp_colour_addb" ] = -0.05 * r_var
|
||||
tab[ "$pp_colour_brightness" ] = 0 -r_var * 0.15
|
||||
tab[ "$pp_colour_colour" ] = 1 - r_var * 0.5 -- We're not good at seeing colors in the dark.
|
||||
tab[ "$pp_colour_contrast" ] = 1 + r_var * 0.08 -- Lower the contrast, however; Bright things are still bright
|
||||
DrawColorModify( tab )
|
||||
end)
|
||||
end
|
||||
|
||||
-- Dynamic Light
|
||||
local dsize = 3000
|
||||
local dlengh = 9000 - 30
|
||||
mapLights[e_lightdynamic] = {}
|
||||
local dLight = -1
|
||||
local function tick()
|
||||
if not SF_SUN_PROTEX then return end
|
||||
local vis = dLight
|
||||
|
||||
-- While the best option is to delete the project-texture.
|
||||
-- Sadly, doing so would allow another mod to override it, as they're limited.
|
||||
if vis < 0 then return end
|
||||
local sunang = StormFox2.Sun.GetAngle() -- Angle towards sun
|
||||
local p = sunang.p % 360
|
||||
local tL = math.min(255,StormFox2.Thunder.GetLight() or 0)
|
||||
if tL > 0 then
|
||||
p = 270
|
||||
sunang.p = p
|
||||
vis = tL / 2
|
||||
else
|
||||
if p > 350 then -- 15 before
|
||||
SF_SUN_PROTEX:SetBrightness(0)
|
||||
SF_SUN_PROTEX:Update()
|
||||
return
|
||||
elseif p > 345 then
|
||||
vis = vis * math.Clamp(-p / 5 + 70, 0, 1)
|
||||
elseif p <= 190 then
|
||||
SF_SUN_PROTEX:SetBrightness(0)
|
||||
SF_SUN_PROTEX:Update()
|
||||
return
|
||||
elseif p <= 195 then
|
||||
vis = vis * math.Clamp(p / 5 - 38, 0, 1)
|
||||
end
|
||||
end
|
||||
local sunnorm = sunang:Forward() -- Norm of sun
|
||||
local viewpos = StormFox2.util.RenderPos() -- Point of render
|
||||
|
||||
local pos = viewpos + sunnorm * dlengh
|
||||
pos.x = math.Round(pos.x / 50) * 50
|
||||
pos.y = math.Round(pos.y / 50) * 50
|
||||
|
||||
SF_SUN_PROTEX:SetPos(pos)
|
||||
if math.Round(sunang.p) == 0 then -- All source light gets a bit, glitchy at 0 or 180 pitch
|
||||
SF_SUN_PROTEX:SetAngles(Angle(179,sunang.y,0))
|
||||
else
|
||||
SF_SUN_PROTEX:SetAngles(Angle(sunang.p + 180,sunang.y,0))
|
||||
end
|
||||
SF_SUN_PROTEX:SetBrightness(vis * 2)
|
||||
SF_SUN_PROTEX:Update()
|
||||
end
|
||||
mapLights[e_lightdynamic]["on"] = function(lightLvl)
|
||||
if SERVER then
|
||||
SetLightStyle( 'b' )
|
||||
SetLightEnv('b')
|
||||
StormFox2.Shadows.SetDisabled( true )
|
||||
else
|
||||
RunConsoleCommand("r_flashlightdepthres", 8192)
|
||||
dLight = lightLvl
|
||||
if IsValid(SF_SUN_PROTEX) then
|
||||
SF_SUN_PROTEX:Remove()
|
||||
end
|
||||
SF_SUN_PROTEX = ProjectedTexture()
|
||||
SF_SUN_PROTEX:SetTexture("stormfox2/effects/dynamic_light")
|
||||
SF_SUN_PROTEX:SetOrthographic( true , dsize, dsize, dsize, dsize)
|
||||
SF_SUN_PROTEX:SetNearZ(0)
|
||||
SF_SUN_PROTEX:SetFarZ( 12000 )
|
||||
SF_SUN_PROTEX:SetQuadraticAttenuation( 0 )
|
||||
SF_SUN_PROTEX:SetShadowDepthBias(0.000005)
|
||||
SF_SUN_PROTEX:SetShadowFilter(0.05) -- Meed tp blur the shadows a bit.
|
||||
SF_SUN_PROTEX:SetShadowSlopeScaleDepthBias(2)
|
||||
SF_SUN_PROTEX:SetEnableShadows(true)
|
||||
hook.Add("Think", "StormFox2.MapLightDynamic", tick)
|
||||
end
|
||||
end
|
||||
mapLights[e_lightdynamic]["off"] = function(lightLvl)
|
||||
if SERVER then
|
||||
SetLightStyle( 'm' )
|
||||
SetLightEnv('m')
|
||||
StormFox2.Shadows.SetDisabled( false )
|
||||
else
|
||||
dLight = 0
|
||||
if SF_SUN_PROTEX then
|
||||
SF_SUN_PROTEX:Remove()
|
||||
SF_SUN_PROTEX = nil
|
||||
end
|
||||
hook.Remove("Think", "StormFox2.MapLightDynamic")
|
||||
end
|
||||
end
|
||||
if CLIENT then
|
||||
mapLights[e_lightdynamic]["change"] = function(lightlvl)
|
||||
dLight = lightlvl
|
||||
end
|
||||
end
|
||||
-- MapLight functions
|
||||
local function EnableMapLight(str, lightlvl)
|
||||
if not mapLights[str] then
|
||||
error("Unknown light")
|
||||
end
|
||||
if not mapLights[str]["on"] then return end
|
||||
mapLights[str]["on"](lightlvl)
|
||||
end
|
||||
local function DisableMapLight(str, lightlvl)
|
||||
if not mapLights[str] then
|
||||
error("Unknown light")
|
||||
end
|
||||
if not mapLights[str]["off"] then return end
|
||||
mapLights[str]["off"](lightlvl)
|
||||
end
|
||||
local function ChangeMapLight(str, lightlvl, full)
|
||||
if not mapLights[str] then
|
||||
error("Unknown light")
|
||||
end
|
||||
if not mapLights[str]["change"] then return end
|
||||
mapLights[str]["change"](lightlvl, full)
|
||||
end
|
||||
-- Function that will remember and enable / disable a setting.
|
||||
local function checkSetting(e_type, bool, lightlvl)
|
||||
if bool and lastSetting[e_type] then return end
|
||||
if not bool and not lastSetting[e_type] then return end
|
||||
if bool then
|
||||
EnableMapLight(e_type, lightlvl)
|
||||
else
|
||||
DisableMapLight(e_type, lightlvl)
|
||||
end
|
||||
lastSetting[e_type] = bool
|
||||
end
|
||||
-- Called when one of the settings change
|
||||
local function SettingMapLight( lightlvl )
|
||||
-- Stop all light-settings when SF gets turned off.
|
||||
if not StormFox2.Setting.GetCache("enable", true) then
|
||||
checkSetting(e_lightstyle, false, lightlvl)
|
||||
checkSetting(e_colormod, false, lightlvl)
|
||||
checkSetting(e_lightdynamic,false, lightlvl)
|
||||
checkSetting(e_light_env, false, lightlvl)
|
||||
return
|
||||
end
|
||||
-- Choose e_light_env or e_colormod
|
||||
if StormFox2.Setting.Get("maplight_auto") then
|
||||
checkSetting(e_lightdynamic,false, lightlvl)
|
||||
checkSetting(e_lightstyle, false, lightlvl)
|
||||
if StormFox2.Ent.light_environments then
|
||||
checkSetting(e_colormod, false, lightlvl)
|
||||
checkSetting(e_light_env, true, lightlvl)
|
||||
else
|
||||
checkSetting(e_light_env, false, lightlvl)
|
||||
checkSetting(e_colormod, true, lightlvl)
|
||||
end
|
||||
else
|
||||
-- Can be enabled for all
|
||||
checkSetting(e_colormod, StormFox2.Setting.Get("maplight_colormod", false), lightlvl)
|
||||
-- Choose dynamic or lightstyle
|
||||
if StormFox2.Setting.Get("maplight_dynamic", false) then
|
||||
checkSetting(e_lightstyle, false, lightlvl)
|
||||
checkSetting(e_light_env, false, lightlvl)
|
||||
checkSetting(e_lightdynamic,true, lightlvl)
|
||||
else
|
||||
checkSetting(e_lightdynamic,false, lightlvl)
|
||||
checkSetting(e_lightstyle, StormFox2.Setting.Get("maplight_lightstyle", false),lightlvl)
|
||||
checkSetting(e_light_env, StormFox2.Setting.Get("maplight_lightenv", false), lightlvl)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Called when lightlvl has changed
|
||||
local function ChangedMapLight( lightlvl, isSmoothLight)
|
||||
local LastUpdate = not isSmoothLight
|
||||
if StormFox2.Setting.GetCache("maplight_auto", true) then
|
||||
if StormFox2.Ent.light_environments then
|
||||
ChangeMapLight(e_light_env, lightlvl, LastUpdate)
|
||||
return true
|
||||
else
|
||||
ChangeMapLight(e_colormod, lightlvl, LastUpdate)
|
||||
end
|
||||
else
|
||||
local a = false
|
||||
if StormFox2.Setting.GetCache("maplight_dynamic", false) then
|
||||
ChangeMapLight(e_lightdynamic, lightlvl, LastUpdate)
|
||||
a = true
|
||||
end
|
||||
if StormFox2.Setting.GetCache("maplight_lightstyle", false) then
|
||||
ChangeMapLight(e_lightstyle, lightlvl, LastUpdate)
|
||||
a = true
|
||||
end
|
||||
if StormFox2.Setting.GetCache("maplight_lightenv", false) then
|
||||
ChangeMapLight(e_light_env, lightlvl, LastUpdate)
|
||||
a = true
|
||||
end
|
||||
if StormFox2.Setting.GetCache("maplight_colormod", false) then
|
||||
ChangeMapLight(e_colormod, lightlvl, LastUpdate)
|
||||
end
|
||||
return a
|
||||
end
|
||||
end
|
||||
|
||||
-- Sets the detail-light
|
||||
local SetDetailLight
|
||||
if CLIENT then
|
||||
-- Detail MapLight
|
||||
-- Use default detail material (Just in case)
|
||||
local detailstr = {["detail/detailsprites"] = true}
|
||||
-- Find map detail from BSP
|
||||
local mE = StormFox2.Map.Entities()[1]
|
||||
if mE and mE["detailmaterial"] then
|
||||
detailstr[mE["detailmaterial"]] = true
|
||||
end
|
||||
-- Add EP2 by default
|
||||
local ep2m = Material("detail/detailsprites_ep2")
|
||||
if ep2m and not ep2m:IsError() then
|
||||
detailstr["detail/detailsprites_ep2"] = true
|
||||
end
|
||||
local detail = {}
|
||||
for k,v in pairs(detailstr) do
|
||||
table.insert(detail, (Material(k)))
|
||||
end
|
||||
SetDetailLight = function(lightAmount)
|
||||
lightAmount = math.Clamp(lightAmount / 100, 0, 1)
|
||||
local v = Vector(lightAmount,lightAmount,lightAmount)
|
||||
for i, m in ipairs(detail) do
|
||||
m:SetVector("$color",v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns light-variables
|
||||
local f_mapLight = StormFox2.Setting.GetCache("maplight_max",80)
|
||||
local f_mapLightRaw = 100
|
||||
local c_last_char = 'm'
|
||||
---Returns the current light-amount.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Map.GetLight()
|
||||
return f_mapLight
|
||||
end
|
||||
|
||||
---Returns the current raw light-amount. Ignores settings.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Map.GetLightRaw()
|
||||
return f_mapLightRaw
|
||||
end
|
||||
|
||||
---Returns the current light-char. Source use letters to indecate light.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Map.GetLightChar()
|
||||
return c_last_char
|
||||
end
|
||||
|
||||
local function getMaxLight(curLight)
|
||||
if curLight <= 0 then return 0 end
|
||||
local n = StormFox2.Setting.GetCache("maplight_max",80)
|
||||
if n <= 0 then return 0 end
|
||||
return math.Clamp(curLight / n, 0, 1) * 100
|
||||
end
|
||||
|
||||
-- On launch. Setup light
|
||||
local init = false
|
||||
do
|
||||
local chicken, egg = false, false
|
||||
local function tryInit()
|
||||
if not chicken or not egg then return end
|
||||
SettingMapLight(f_mapLight)
|
||||
hook.Run("StormFox2.lightsystem.new", f_mapLight, getMaxLight(f_mapLight))
|
||||
if CLIENT then SetDetailLight(f_mapLight) end
|
||||
init = true
|
||||
end
|
||||
hook.Add("StormFox2.PostEntityScan", "stormfox2.lightsystem.init", function()
|
||||
chicken = true
|
||||
tryInit()
|
||||
end)
|
||||
hook.Add("stormfox2.postinit", "stormfox2.lightsystem.init2", function()
|
||||
egg = true
|
||||
tryInit()
|
||||
end)
|
||||
end
|
||||
-- On settings change
|
||||
for _, conv in ipairs({"enable","maplight_auto", "maplight_lightenv", "maplight_colormod", "maplight_dynamic", "maplight_lightstyle"}) do
|
||||
StormFox2.Setting.Callback(conv,function(var)
|
||||
SettingMapLight(f_mapLight)
|
||||
end, conv .. "MLCheck")
|
||||
end
|
||||
|
||||
-- Allows us to use SetLight, without removing the lerp.
|
||||
local lil = true
|
||||
local function SetLightInternal(f, isSmoothLight)
|
||||
if f < 0 then f = 0 elseif
|
||||
f > 100 then f = 100 end
|
||||
if f_mapLight == f and lil == isSmoothLight then return end -- Ignore
|
||||
f_mapLight = f
|
||||
lil = isSmoothLight
|
||||
c_last_char = convertToAZ(f)
|
||||
if not init then return end
|
||||
-- 2D Skybox
|
||||
if SERVER then
|
||||
local str = StormFox2.Setting.GetCache("overwrite_2dskybox","")
|
||||
local use_2d = StormFox2.Setting.GetCache("use_2dskybox",false)
|
||||
if use_2d and str ~= "painted" then
|
||||
StormFox2.Map.Set2DSkyBoxDarkness( f * 0.009 + 0.1, true )
|
||||
end
|
||||
end
|
||||
-- SetMapLight
|
||||
ChangedMapLight(f, isSmoothLight)
|
||||
if CLIENT then SetDetailLight(f) end
|
||||
-- Tell scripts to update
|
||||
hook.Run("StormFox2.lightsystem.new", f, getMaxLight(f))
|
||||
end
|
||||
|
||||
local t = {}
|
||||
|
||||
---Sets the maplight using a number between 0 - 100. last_update should be true, if we aren't lerping.
|
||||
---Clients need to run this too for internal stuff, but won't change the maplight.
|
||||
---@param int number
|
||||
---@param last_update boolean
|
||||
---@shared
|
||||
function StormFox2.Map.SetLight( int, last_update )
|
||||
t = {} -- Remove light lerping
|
||||
SetLightInternal(int, last_update)
|
||||
end
|
||||
|
||||
--[[ Lerp light
|
||||
People complain if we use lightStyle too much (Even with settings), so I've removed lerp from maps without light_environment.
|
||||
]]
|
||||
|
||||
---Lerps the light towards the goal. Make "isSmooth" false if you're calling it rapidly.
|
||||
---@param int number
|
||||
---@param nLerpTime number
|
||||
---@param isSmooth boolean
|
||||
---@shared
|
||||
function StormFox2.Map.SetLightLerp(int, nLerpTime, isSmooth )
|
||||
local smooth = StormFox2.Setting.GetCache("maplight_smooth",true)
|
||||
local num = StormFox2.Setting.GetCache("maplight_updaterate", 3)
|
||||
-- No lights to smooth and/or setting is off
|
||||
local _5sec = 0.08 * StormFox2.Time.GetSpeed_RAW()
|
||||
t = {}
|
||||
if not smooth or nLerpTime <= _5sec or not f_mapLight or num <= 1 then
|
||||
SetLightInternal( int )
|
||||
return
|
||||
end
|
||||
-- Are we trying to lerp towards current value?
|
||||
if f_mapLight and f_mapLight == int then
|
||||
return
|
||||
end
|
||||
-- Start lerping ..
|
||||
-- We make a time-list of said values.
|
||||
local st = StormFox2.Time.Get() -- Start Time
|
||||
local st_lerpt = nLerpTime / num -- Each "step"'s time
|
||||
-- Too fast of a light change. Can bug out.
|
||||
if st_lerpt < 5 then -- Set the each step to min 5 seconds.
|
||||
st_lerpt = 5
|
||||
num = math.floor(nLerpTime / 5)
|
||||
if num <= 1 then -- Only change once.
|
||||
SetLightInternal( int )
|
||||
return
|
||||
end
|
||||
end
|
||||
local st_lerp = math.abs(f_mapLight - int) / num -- Each "step"'s value
|
||||
-- from: f_mapLight
|
||||
-- to: f
|
||||
for i = 0, num - 1 do
|
||||
table.insert(t, {
|
||||
(st + (i * st_lerpt)) % 1440, -- Time when applied
|
||||
math.floor(math.Approach(f_mapLight, int, st_lerp * (i + 1))),-- The light value
|
||||
i ~= num - 1 or isSmooth -- Isn't last
|
||||
})
|
||||
end
|
||||
--print("From:",f_mapLight, "TO:", f, "step:",st_lerp, "nums:",num)
|
||||
--StormFox2.Map.SetLight( math.Approach(f_mapLight, f, n), true )
|
||||
end
|
||||
timer.Create("StormFox2.lightupdate", 2, 0, function()
|
||||
if #t <= 0 then return end
|
||||
local n = t[1]
|
||||
local time = StormFox2.Time.Get()
|
||||
if n[1] > time or math.abs(time - n[1]) > 720 then return end -- Wait.
|
||||
-- Trigger
|
||||
local v = table.remove(t, 1)
|
||||
SetLightInternal( v[2], v[3] ) -- Set the light, and lightsystel if last.
|
||||
--print("SetLight", v[2], v[3])
|
||||
end)
|
||||
if SERVER then
|
||||
-- Control light
|
||||
hook.Add("StormFox2.weather.postchange", "StormFox2.weather.setlight", function( sName ,nPercentage, nDelta )
|
||||
local night, day
|
||||
if StormFox2.Setting.GetCache("allow_weather_lightchange") then
|
||||
night,day = StormFox2.Data.GetFinal("mapNightLight", 0), StormFox2.Data.GetFinal("mapDayLight",100) -- Maplight
|
||||
else
|
||||
local c = StormFox2.Weather.Get("Clear")
|
||||
night,day = c:Get("mapNightLight",0), c:Get("mapDayLight",80) -- Maplight
|
||||
end
|
||||
local minlight,maxlight = StormFox2.Setting.GetCache("maplight_min",0),StormFox2.Setting.GetCache("maplight_max",80) -- Settings
|
||||
local smooth = StormFox2.Setting.GetCache("maplight_smooth",game.SinglePlayer())
|
||||
-- Calc maplight
|
||||
local isSmooth = false
|
||||
local stamp, mapLight = StormFox2.Sky.GetLastStamp()
|
||||
if stamp >= SF_SKY_CEVIL then
|
||||
mapLight = night
|
||||
elseif stamp <= SF_SKY_DAY then
|
||||
mapLight = day
|
||||
else
|
||||
local delta = math.abs( SF_SKY_DAY - SF_SKY_CEVIL )
|
||||
local f = StormFox2.Sky.GetLastStamp() / delta
|
||||
if smooth then
|
||||
mapLight = Lerp((f + 0.5) / 2, day, night)
|
||||
isSmooth = true
|
||||
elseif f <= 0.5 then
|
||||
mapLight = day
|
||||
else
|
||||
mapLight = night
|
||||
end
|
||||
end
|
||||
f_mapLightRaw = mapLight
|
||||
-- Apply settings
|
||||
local newLight = minlight + mapLight * (maxlight - minlight) / 100
|
||||
local sec = 15 * StormFox2.Time.GetSpeed_RAW()
|
||||
StormFox2.Map.SetLightLerp(newLight, math.min(sec, nDelta or sec), isSmooth )
|
||||
end)
|
||||
|
||||
-- Min and maxlight hotupdate
|
||||
local function hotUpdate()
|
||||
local night, day
|
||||
if StormFox2.Setting.GetCache("allow_weather_lightchange") then
|
||||
night,day = StormFox2.Data.GetFinal("mapNightLight", 0), StormFox2.Data.GetFinal("mapDayLight",100) -- Maplight
|
||||
else
|
||||
local c = StormFox2.Weather.Get("Clear")
|
||||
night,day = c:Get("mapNightLight",0), c:Get("mapDayLight",80) -- Maplight
|
||||
end
|
||||
local minlight,maxlight = StormFox2.Setting.GetCache("maplight_min",0),StormFox2.Setting.GetCache("maplight_max",80) -- Settings
|
||||
local smooth = StormFox2.Setting.GetCache("maplight_smooth",game.SinglePlayer())
|
||||
-- Calc maplight
|
||||
local isSmooth = false
|
||||
local stamp, mapLight = StormFox2.Sky.GetLastStamp()
|
||||
if stamp >= SF_SKY_CEVIL then
|
||||
mapLight = night
|
||||
elseif stamp <= SF_SKY_DAY then
|
||||
mapLight = day
|
||||
else
|
||||
local delta = math.abs( SF_SKY_DAY - SF_SKY_CEVIL )
|
||||
local f = StormFox2.Sky.GetLastStamp() / delta
|
||||
if smooth then
|
||||
mapLight = Lerp((f + 0.5) / 2, day, night)
|
||||
isSmooth = true
|
||||
elseif f <= 0.5 then
|
||||
mapLight = day
|
||||
else
|
||||
mapLight = night
|
||||
end
|
||||
end
|
||||
f_mapLightRaw = mapLight
|
||||
-- Apply settings
|
||||
local newLight = minlight + mapLight * (maxlight - minlight) / 100
|
||||
StormFox2.Map.SetLight( newLight )
|
||||
end
|
||||
StormFox2.Setting.Callback("maplight_min", hotUpdate)
|
||||
StormFox2.Setting.Callback("maplight_max", hotUpdate)
|
||||
|
||||
else -- Fake darkness. Since some maps are bright
|
||||
|
||||
hook.Add("StormFox2.weather.postchange", "StormFox2.weather.setlight", function( sName ,nPercentage, nDelta )
|
||||
if not StormFox2.Map or not StormFox2.Map.SetLightLerp then return end
|
||||
local minlight,maxlight = StormFox2.Setting.GetCache("maplight_min",0),StormFox2.Setting.GetCache("maplight_max",80) -- Settings
|
||||
local smooth = StormFox2.Setting.GetCache("maplight_smooth",game.SinglePlayer())
|
||||
local night, day
|
||||
if StormFox2.Setting.GetCache("allow_weather_lightchange") then
|
||||
night,day = StormFox2.Data.GetFinal("mapNightLight", 0), StormFox2.Data.GetFinal("mapDayLight",100) -- Maplight
|
||||
else
|
||||
local c = StormFox2.Weather.Get("Clear")
|
||||
night,day = c:Get("mapNightLight",0), c:Get("mapDayLight",80) -- Maplight
|
||||
end
|
||||
-- Calc maplight
|
||||
local isSmooth = false
|
||||
local stamp, mapLight = StormFox2.Sky.GetLastStamp()
|
||||
if stamp >= SF_SKY_CEVIL then
|
||||
mapLight = night
|
||||
elseif stamp <= SF_SKY_DAY then
|
||||
mapLight = day
|
||||
else
|
||||
local delta = math.abs( SF_SKY_DAY - SF_SKY_CEVIL )
|
||||
local f = StormFox2.Sky.GetLastStamp() / delta
|
||||
if smooth then
|
||||
mapLight = Lerp((f + 0.5) / 2, day, night)
|
||||
isSmooth = true
|
||||
elseif f <= 0.5 then
|
||||
mapLight = day
|
||||
else
|
||||
mapLight = night
|
||||
end
|
||||
end
|
||||
f_mapLightRaw = mapLight
|
||||
-- Apply settings
|
||||
local newLight = minlight + mapLight * (maxlight - minlight) / 100
|
||||
local sec = 15 * StormFox2.Time.GetSpeed_RAW()
|
||||
StormFox2.Map.SetLightLerp(newLight, math.min(sec, nDelta or sec), isSmooth )
|
||||
end)
|
||||
|
||||
local function exp(n)
|
||||
return n * n
|
||||
end
|
||||
local mat_screen = Material( "stormfox2/shader/pp_dark" )
|
||||
local mat_ColorMod = Material( "stormfox2/shader/color" )
|
||||
mat_ColorMod:SetTexture( "$fbtexture", render.GetScreenEffectTexture() )
|
||||
local texMM = GetRenderTargetEx( "_SF_DARK", -1, -1, RT_SIZE_FULL_FRAME_BUFFER, MATERIAL_RT_DEPTH_NONE, 0, 0, IMAGE_FORMAT_RGB888 )
|
||||
|
||||
-- Renders pp_dark
|
||||
local function UpdateStencil( darkness )
|
||||
if not render.SupportsPixelShaders_2_0() then return end -- How old is the GPU!?
|
||||
render.UpdateScreenEffectTexture()
|
||||
render.PushRenderTarget(texMM)
|
||||
render.Clear( 255 * darkness, 255 * darkness, 255 * darkness, 255 * darkness )
|
||||
render.ClearDepth()
|
||||
render.OverrideBlend( true, BLEND_ONE_MINUS_SRC_COLOR, BLEND_ONE_MINUS_SRC_COLOR, 0, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD )
|
||||
render.SetMaterial(mat_ColorMod)
|
||||
render.DrawScreenQuad()
|
||||
render.OverrideBlend( false )
|
||||
render.PopRenderTarget()
|
||||
mat_screen:SetTexture( "$basetexture", texMM )
|
||||
end
|
||||
local fade = 0
|
||||
hook.Add("RenderScreenspaceEffects","StormFox2.Light.MapMat",function()
|
||||
if not StormFox2.Map.GetLightRaw then return end
|
||||
if not StormFox2.Setting.SFEnabled() then return end
|
||||
-- How old is the GPU!?
|
||||
if not render.SupportsPixelShaders_2_0() then return end
|
||||
local a = 1 - StormFox2.Map.GetLightRaw() / 100
|
||||
if a <= 0 then -- Too bright
|
||||
fade = 0
|
||||
return
|
||||
end
|
||||
-- Check settings
|
||||
local scale = StormFox2.Setting.GetCache("overwrite_extra_darkness",-1)
|
||||
if scale == 0 then return end -- Force off.
|
||||
if scale < 0 then
|
||||
if not StormFox2.Setting.GetCache("extra_darkness",true) then return end
|
||||
scale = StormFox2.Setting.GetCache("extra_darkness_amount",1)
|
||||
end
|
||||
if scale <= 0 then return end
|
||||
-- Calc the "fade" between outside and inside
|
||||
local t = StormFox2.Environment.Get()
|
||||
if t.outside then
|
||||
fade = math.min(2, fade + FrameTime())
|
||||
elseif t.nearest_outside then
|
||||
-- Calc dot
|
||||
local view = StormFox2.util.GetCalcView()
|
||||
if not view then return end
|
||||
local d = view.pos:DistToSqr(t.nearest_outside)
|
||||
if d < 15000 then
|
||||
fade = math.min(2, fade + FrameTime())
|
||||
elseif d > 40000 then -- Too far away
|
||||
fade = math.max(0, fade - FrameTime())
|
||||
else
|
||||
local v1 = view.ang:Forward()
|
||||
local v2 = (t.nearest_outside - view.pos):GetNormalized()
|
||||
if v1:Dot(v2) < 0.6 then -- You're looking away
|
||||
fade = math.max(0, fade - FrameTime())
|
||||
else -- You're looking at it
|
||||
fade = math.min(2, fade + FrameTime())
|
||||
end
|
||||
end
|
||||
else
|
||||
fade = math.max(0, fade - FrameTime())
|
||||
end
|
||||
if fade <= 0 then return end
|
||||
-- Render
|
||||
UpdateStencil(exp(a * scale * math.min(1, fade)))
|
||||
render.SetMaterial(mat_screen)
|
||||
local w,h = ScrW(),ScrH()
|
||||
render.OverrideBlend( true, 0, BLEND_ONE_MINUS_SRC_COLOR, 2, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD )
|
||||
render.DrawScreenQuadEx(0,0,w,h)
|
||||
render.OverrideBlend( false )
|
||||
end)
|
||||
end
|
||||
|
||||
--[[
|
||||
TODO:
|
||||
1) Add option to limit the angle of the shadows.
|
||||
2) Shadow color to match skies?
|
||||
]]
|
||||
155
lua/stormfox2/framework/sh_mixer.lua
Normal file
155
lua/stormfox2/framework/sh_mixer.lua
Normal file
@@ -0,0 +1,155 @@
|
||||
--[[
|
||||
| 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 mixed updates the variables live, unlike the Data.
|
||||
-- This can't be set tho.
|
||||
StormFox2.Mixer = {}
|
||||
|
||||
-- Local function
|
||||
local function isColor(t)
|
||||
return t.r and t.g and t.b and true or false
|
||||
end
|
||||
|
||||
---Tries to blend two variables together. Will return first variable if unable to.
|
||||
---@param nFraction number
|
||||
---@param vFrom any
|
||||
---@param vTo any
|
||||
---@return any
|
||||
---@shared
|
||||
local function Blender(nFraction, vFrom, vTo)
|
||||
-- Will it blend?
|
||||
-- Nils should be false, if one of them is a boolean
|
||||
if type(vFrom) == "nil" and type(vTo) == "boolean" then
|
||||
vFrom = false
|
||||
end
|
||||
if type(vTo) == "nil" and type(vFrom) == "boolean" then
|
||||
vTo = false
|
||||
end
|
||||
-- If the same value, then return it
|
||||
if vTo == vFrom then return vTo end
|
||||
-- In case of two diffrent variables.
|
||||
if type(vFrom) ~= type(vTo) then
|
||||
StormFox2.Warning("Mixer called with values of two different types[" .. type(vFrom) .. "," .. type(vTo) .. "]")
|
||||
debug.Trace()
|
||||
return vFrom
|
||||
elseif type(vTo) == "string" or type(vTo) == "IMaterial" or type(vTo) == "boolean" then -- String, material or bool. Return vTo.
|
||||
return vTo
|
||||
elseif type(vTo) == "number" then -- Number
|
||||
return Lerp(nFraction, vFrom, vTo)
|
||||
elseif type(vTo) == "table" then -- Objects
|
||||
local t = vTo.__MetaName and vTo.__MetaName == "CCT_Color" or false
|
||||
local f = vFrom.__MetaName and vFrom.__MetaName == "CCT_Color" or false
|
||||
if t and f then
|
||||
local v = StormFox2.util.CCTColor( Lerp( nFraction, vFrom:GetKelvin(), vTo:GetKelvin() ) )
|
||||
return v:ToRGB()
|
||||
else
|
||||
local s = f and vFrom:ToRGB() or vFrom
|
||||
local e = t and vTo:ToRGB() or vTo
|
||||
local r = Lerp( nFraction, s.r or 255, e.r )
|
||||
local g = Lerp( nFraction, s.g or 255, e.g )
|
||||
local b = Lerp( nFraction, s.b or 255, e.b )
|
||||
local a = Lerp( nFraction, s.a or 255, e.a )
|
||||
return Color( r, g, b, a )
|
||||
end
|
||||
end
|
||||
--StormFox2.Warning("ERROR: Unsupported mix value type[" .. type(vTo) .. "]. Returning original value")
|
||||
--debug.Trace()
|
||||
return vFrom
|
||||
end
|
||||
|
||||
local cache = {}
|
||||
local cStamp,nStamp,nStampFraction = 0,0,0
|
||||
local function GetVar( wWeather, sKey )
|
||||
local v1 = wWeather:Get(sKey, cStamp)
|
||||
if cStamp == nStamp or nStampFraction <= 0 then
|
||||
return v1
|
||||
end
|
||||
local v2 = wWeather:Get(sKey, nStamp)
|
||||
local v = Blender(nStampFraction, v1, v2)
|
||||
return v
|
||||
end
|
||||
|
||||
StormFox2.Mixer.Blender = Blender
|
||||
|
||||
local function vOd(a, b)
|
||||
if a == nil then return b end
|
||||
return a
|
||||
end
|
||||
|
||||
---Blends the current weather key-variables together. Will return zDefault if fail. The result will be cached, unless you set the currentP variable.
|
||||
---Mixer allows for live-precise variables.
|
||||
---@param sKey string
|
||||
---@param zDefault any
|
||||
---@param currentP? number
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Mixer.Get( sKey, zDefault, currentP )
|
||||
if not currentP and cache[sKey] ~= nil then return cache[sKey] end
|
||||
if not StormFox2.Weather then return zDefault end -- Not loaded yet
|
||||
-- Get the current weather
|
||||
local cW = StormFox2.Weather.GetCurrent()
|
||||
-- In case thw weather-type is clear, no need to calculate.
|
||||
if not cW or cW.Name == "Clear" then return vOd( GetVar(cW, sKey), zDefault) end
|
||||
-- Get the percent, and check if we should cache.
|
||||
local shouldCache = not currentP
|
||||
currentP = currentP or StormFox2.Weather.GetPercent()
|
||||
if currentP >= 1 then -- No need to mix things, weather is at max.
|
||||
if shouldCache then
|
||||
cache[sKey] = GetVar(cW, sKey)
|
||||
return vOd(cache[sKey], zDefault)
|
||||
else
|
||||
return vOd(GetVar(cW, sKey), zDefault)
|
||||
end
|
||||
end
|
||||
-- Get the default weather to mix with.
|
||||
local clearW = StormFox2.Weather.Get( "Clear" )
|
||||
local var1 = GetVar(clearW, sKey)
|
||||
local var2 = GetVar(cW, sKey)
|
||||
if shouldCache then
|
||||
cache[sKey] = Blender(currentP, var1, var2)
|
||||
return vOd(cache[sKey], zDefault)
|
||||
else
|
||||
return vOd(Blender(currentP, var1, var2), zDefault)
|
||||
end
|
||||
end
|
||||
StormFox2.Mixer.Blender = Blender
|
||||
|
||||
--[[t.Function = {}
|
||||
t.Static = {}
|
||||
t.Dynamic = {}
|
||||
t.SunStamp = {}
|
||||
]]
|
||||
|
||||
-- Resets the values after a few frames. This is calculated live and should be cached.
|
||||
local max_frames = 4
|
||||
local i = 0
|
||||
local percent = 0
|
||||
hook.Add("Think", "StormFox2.mixerreset", function()
|
||||
i = i + 1
|
||||
if i < max_frames then return end
|
||||
i = 0
|
||||
cache = {}
|
||||
-- Current Stamp
|
||||
local nTime = StormFox2.Time.Get()
|
||||
local stamp, percent, next_stamp, pitch_length = StormFox2.Sky.GetStamp(nTime, nil, true) -- cpercent goes from 0 - 1
|
||||
if not pitch_length then return end
|
||||
local pitch_left = pitch_length * (1 - percent)
|
||||
local forward = 6
|
||||
if pitch_left >= 6 then -- Only look 6 degrees in the furture.
|
||||
cStamp = stamp
|
||||
nStamp = stamp
|
||||
nStampFraction = 0
|
||||
else
|
||||
cStamp = stamp
|
||||
nStamp = next_stamp
|
||||
nStampFraction = 1 - ( pitch_left / (math.min(pitch_length, 6)) )
|
||||
end
|
||||
end)
|
||||
146
lua/stormfox2/framework/sh_shadowcontrol.lua
Normal file
146
lua/stormfox2/framework/sh_shadowcontrol.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
--[[
|
||||
| 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("modifyshadows",true,nil, "Effects")
|
||||
StormFox2.Setting.AddSV("modifyshadows_rate",game.IsDedicated() and 2 or 0.25,nil, "Effects", 0, 10)
|
||||
StormFox2.Setting.SetType("modifyshadows_rate", "float")
|
||||
if CLIENT then
|
||||
net.Receive(StormFox2.Net.Shadows, function()
|
||||
timer.Simple(0.2, function()
|
||||
for k, ent in ipairs(ents.GetAll()) do
|
||||
ent:MarkShadowAsDirty()
|
||||
ent:PhysWake()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
if CLIENT then return end
|
||||
StormFox2.Shadows = {}
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Shadow controls
|
||||
---------------------------------------------------------------------------]]
|
||||
local lastP = -1
|
||||
|
||||
---Sets the shadow angles on the map using a given pitch number.
|
||||
---@param nPitch number
|
||||
---@server
|
||||
function StormFox2.Shadows.SetAngle( nPitch )
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
local nPitch = (nPitch + 180) % 360
|
||||
if nPitch == lastP then return end
|
||||
lastP = nPitch
|
||||
local str = nPitch .. " " .. StormFox2.Sun.GetYaw() .. " " .. 0 .. " "
|
||||
for _,ent in ipairs( StormFox2.Ent.shadow_controls ) do
|
||||
ent:Fire( "SetAngles" , str , 0 )
|
||||
end
|
||||
net.Start(StormFox2.Net.Shadows)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
---Sets the shadow color
|
||||
---@param sColor table
|
||||
---@server
|
||||
function StormFox2.Shadows.SetColor( sColor )
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
local s = sColor.r .. " " .. sColor.g .. " " .. sColor.b
|
||||
for _,ent in ipairs( StormFox2.Ent.shadow_controls ) do
|
||||
ent:SetKeyValue( "color", s )
|
||||
end
|
||||
end
|
||||
|
||||
---Sets the shadow distance
|
||||
---@param dis number
|
||||
---@server
|
||||
function StormFox2.Shadows.SetDistance( dis )
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
for _,ent in ipairs( StormFox2.Ent.shadow_controls ) do
|
||||
ent:SetKeyValue( "SetDistance", dis )
|
||||
end
|
||||
end
|
||||
|
||||
---Disable / Enables shadows
|
||||
---@param bool boolean
|
||||
---@server
|
||||
function StormFox2.Shadows.SetDisabled( bool )
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
for _,ent in ipairs( StormFox2.Ent.shadow_controls ) do
|
||||
ent:SetKeyValue( "SetShadowsDisabled", bool and 1 or 0 )
|
||||
end
|
||||
end
|
||||
|
||||
-- Simple function to set the light
|
||||
local n
|
||||
local function SetDarkness(l)
|
||||
if n and n == l then return end
|
||||
n = l
|
||||
local c = 255 - 68 * n
|
||||
StormFox2.Shadows.SetColor( Color(c,c,c) )
|
||||
end
|
||||
|
||||
local l = 0
|
||||
local function shadowTick()
|
||||
-- Limit update rate
|
||||
if l >= CurTime() then return end
|
||||
local rate = StormFox2.Setting.GetCache("modifyshadows_rate", 2)
|
||||
l = CurTime() + rate
|
||||
|
||||
local sP = StormFox2.Sun.GetAngle().p % 360
|
||||
--360 -> 180 Day
|
||||
local c = math.abs(math.AngleDifference(sP, 270)) -- 0 - 180. Above 90 is night.
|
||||
if c > 90 then -- Night
|
||||
StormFox2.Shadows.SetAngle( 270 )
|
||||
else
|
||||
StormFox2.Shadows.SetAngle( sP )
|
||||
end
|
||||
if c < 80 then
|
||||
SetDarkness(1)
|
||||
elseif c < 85 then
|
||||
SetDarkness(17 - 0.2*c)
|
||||
elseif c < 95 then
|
||||
SetDarkness(0)
|
||||
elseif c < 100 then
|
||||
SetDarkness(0.1*c-9.5)
|
||||
else
|
||||
SetDarkness(0)
|
||||
end
|
||||
end
|
||||
|
||||
local function enable()
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
hook.Add("Think", "StormFox2.shadow.rate", shadowTick)
|
||||
end
|
||||
local function disable()
|
||||
hook.Remove("Think", "StormFox2.shadow.rate")
|
||||
if not StormFox2.Ent.shadow_controls then return end
|
||||
local a = StormFox2.Map.FindClass('shadow_control')
|
||||
if not a or #a < 1 then
|
||||
StormFox2.Shadows.SetAngle( 270 )
|
||||
StormFox2.Shadows.SetColor( Color(187, 187, 187) )
|
||||
else
|
||||
local p = (a[1]["angles"] or Angle(90,0,0)).p + 180
|
||||
StormFox2.Shadows.SetAngle( p )
|
||||
local c = string.Explode(" ", a[1]["color"] or "187 187 187")
|
||||
StormFox2.Shadows.SetColor( Color(c[1],c[2],c[3]) )
|
||||
end
|
||||
end
|
||||
|
||||
if StormFox2.Setting.Get("modifyshadows", true) then
|
||||
enable()
|
||||
end
|
||||
StormFox2.Setting.Callback("modifyshadows",function(b)
|
||||
if b then
|
||||
enable()
|
||||
else
|
||||
disable()
|
||||
end
|
||||
end)
|
||||
|
||||
266
lua/stormfox2/framework/sh_temperature.lua
Normal file
266
lua/stormfox2/framework/sh_temperature.lua
Normal file
@@ -0,0 +1,266 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- Holds temperature and wind
|
||||
--[[-------------------------------------------------------------------------
|
||||
Temperature is not universal. A few contries cling on to fahrenheit,
|
||||
so we need another function to display the temperature correctly.
|
||||
|
||||
StormFox runs on celsius, but can convert the temperature to whatever you wish.
|
||||
|
||||
Clients have to use these functions:
|
||||
StormFox2.Temperature.GetDisplay() -- Returns the temperature in what their setting is set to.
|
||||
StormFox2.Temperature.GetDisplaySymbol() -- Returns the temperature symbol for their setting.
|
||||
StormFox2.Temperature.GetDisplayDefault() -- Returns the default temperature setting for their country.
|
||||
StormFox2.Temperature.GetDisplayType() -- Returns the temperature setting clients have set to.
|
||||
|
||||
Fun facts:
|
||||
At -90C, we need specialized air or our brain "forgets" to breathe.
|
||||
You'll be unconscious in an hour in 10C water. Dead in 3.
|
||||
At -180C oxygen will liquidfy.
|
||||
Coldest recorded temp on Earth is -90C
|
||||
|
||||
---------------------------------------------------------------------------]]
|
||||
StormFox2.Temperature = {}
|
||||
local convert_from,convert_to = {},{}
|
||||
local p1,p2,p3,p4,p5 = 3 / 2, 33 / 100, 4 / 5, 21 / 40,240 / 54
|
||||
convert_to["fahrenheit"] = function(nC) return nC * 1.8 + 32 end
|
||||
convert_to["kelvin"] = function(nC) return nC + 273.15 end
|
||||
convert_to["rankine"] = function(nC) return (nC + 273.15) * 1.8 end
|
||||
convert_to["delisle"] = function(nC) return (100 - nC) * p1 end
|
||||
convert_to["newton"] = function(nC) return nC * p2 end
|
||||
convert_to["réaumur"] = function(nC) return nC * p3 end
|
||||
convert_to["rømer"] = function(nC) return nC * p4 + 7.5 end
|
||||
convert_to["wedgwood"] = function(nC) return (nC - 580.8) * p5 end
|
||||
convert_to["gas_mark"] = function(nC)
|
||||
if nC >= 135 then
|
||||
return 1 + (nC - 135) / 13.9
|
||||
else
|
||||
return 1 / ((135 - nC) / 13.9 * 2)
|
||||
end
|
||||
end
|
||||
convert_to["banana"] = function(nC) -- 380 kJ of energy in an average size banana. Takes about 344,49kJ to heat up an avage room by 10c. 1 banana = 1.1c
|
||||
return 10 * (nC - 10) / 11
|
||||
end
|
||||
local p1,p2,p3,p4,p5,p6 = 5 / 9, 2 / 3, 100 / 33, 5 / 4, 40 / 21,54 / 240
|
||||
convert_from["fahrenheit"] = function(nF) return (nF - 32) / 1.8 end
|
||||
convert_from["kelvin"] = function(nK) return nK - 273.15 end
|
||||
convert_from["rankine"] = function(nR) return (nR - 491.67) * p1 end
|
||||
convert_from["delisle"] = function(nD) return 100 - nD * p2 end
|
||||
convert_from["newton"] = function(nN) return nN * p3 end
|
||||
convert_from["réaumur"] = function(nR) return nR * p4 end
|
||||
convert_from["rømer"] = function(nR) return (nR - 7.5) * p5 end
|
||||
convert_from["wedgwood"] = function(nW) return (nW * p6) + 580.8 end
|
||||
convert_from["gas_mark"] = function(nG)
|
||||
if nG >= 1 then
|
||||
return 0.1 * (139 * nG + 1211)
|
||||
else
|
||||
return 135 - 6.95 / nG
|
||||
end
|
||||
end
|
||||
convert_from["banana"] = function(nB)
|
||||
return 1.1 * nB + 10
|
||||
end
|
||||
local symbol = {
|
||||
["celsius"] = "°C",
|
||||
["fahrenheit"] = "°F",
|
||||
["rankine"] = "°R",
|
||||
["delisle"] = "°D",
|
||||
["newton"] = "°N",
|
||||
["réaumur"] = "°Ré",
|
||||
["rømer"] = "°Rø",
|
||||
["wedgwood"] = "°W",
|
||||
["gas_mark"] = "°G",
|
||||
["banana"] = "°B",
|
||||
["kelvin"] = "K"
|
||||
}
|
||||
--[[<Shared>------------------------------------------------------------------
|
||||
Returns the current temperature. Valid temperatures:
|
||||
- celsius : default
|
||||
- fahrenheit
|
||||
- kelvin
|
||||
- rankine
|
||||
- delisle
|
||||
- newton
|
||||
- réaumur
|
||||
- rømer
|
||||
---------------------------------------------------------------------------]]
|
||||
local tempOverwrite
|
||||
|
||||
---Returns the current temperature. sType can be "celsius" (default), "fahrenheit", "kelvin" .. ect
|
||||
---@param sType? string
|
||||
---@return Color
|
||||
---@shared
|
||||
function StormFox2.Temperature.Get(sType)
|
||||
local n = tempOverwrite or StormFox2.Data.Get( "Temp", 20 )
|
||||
if not sType or sType == "celsius" then return n end
|
||||
if not convert_to[sType] then
|
||||
StormFox2.Warning("Invalid temperature type [" .. tostring(sType) .. "].", true)
|
||||
end
|
||||
return convert_to[sType](n)
|
||||
end
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Returns the list of valid temperatures.
|
||||
- celsius : default
|
||||
- fahrenheit
|
||||
- kelvin
|
||||
- rankine
|
||||
- delisle
|
||||
- newton
|
||||
- réaumur
|
||||
- rømer
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---Returns all temperature units supported.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Temperature.GetTypes()
|
||||
local t = table.GetKeys(convert_to)
|
||||
table.insert(t,"celsius")
|
||||
return t
|
||||
end
|
||||
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Converts temperature between two types
|
||||
Valid temperatures:
|
||||
- celsius : default
|
||||
- fahrenheit
|
||||
- kelvin
|
||||
- rankine
|
||||
- delisle
|
||||
- newton
|
||||
- réaumur
|
||||
- rømer
|
||||
- wedgwood
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---Converts temperature from one unit to another. E.g StormFox2.Temperature.Convert("celsius","fahrenheit",0) -> 32.
|
||||
---@param sTypeFrom string
|
||||
---@param sTypeTo string
|
||||
---@param nNumber number
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Temperature.Convert(sTypeFrom,sTypeTo,nNumber)
|
||||
if sTypeFrom and sTypeFrom ~= "celsius" then
|
||||
if not convert_from[sTypeFrom] then
|
||||
error("Invalid temperature type [" .. sTypeFrom .. "].")
|
||||
end
|
||||
nNumber = convert_from[sTypeFrom](nNumber)
|
||||
end
|
||||
if sTypeTo and sTypeTo ~= "celsius" then
|
||||
if not convert_to[sTypeTo] then
|
||||
error("Invalid temperature type [" .. sTypeTo .. "].")
|
||||
end
|
||||
nNumber = convert_to[sTypeTo](nNumber)
|
||||
end
|
||||
return nNumber
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
---Sets the temperature in ceilsius. Second argument is the lerp time in seconds (default: 2).
|
||||
---@param nCelsius number
|
||||
---@param nLerpTime? number
|
||||
---@server
|
||||
function StormFox2.Temperature.Set(nCelsius,nLerpTime)
|
||||
if nCelsius < -273.15 then -- ( In space, there are 270.45 C )
|
||||
nCelsius = -273.15
|
||||
end
|
||||
StormFox2.Network.Set("Temp",nCelsius,nLerpTime or 2 * StormFox2.Time.GetSpeed_RAW())
|
||||
end
|
||||
else
|
||||
local country = system.GetCountry() or "UK"
|
||||
local fahrenheit_countries = {"BS","PW","BZ","KY","FM","MH","US","PR","VI","GU"}
|
||||
--[[Bahamas, Palau, Belize, the Cayman Islands, the Federated States of Micronesia, the Marshall Islands,
|
||||
and the United States and its territories such as Puerto Rico, the U.S. Virgin Islands, and Guam.
|
||||
]]
|
||||
local default_temp = table.HasValue(fahrenheit_countries, country) and "fahrenheit" or "celsius"
|
||||
local temp_type = default_temp
|
||||
--[[<Client>------------------------------------------------------------------
|
||||
Sets the display temperature. Returns true if given a valid temperature-type.
|
||||
Valid temperatures:
|
||||
- celsius : default
|
||||
- fahrenheit
|
||||
- kelvin
|
||||
- rankine
|
||||
- delisle
|
||||
- newton
|
||||
- réaumur
|
||||
- rømer
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---Sets the display temperature.
|
||||
---@param sType string
|
||||
---@return boolean success
|
||||
---@client
|
||||
function StormFox2.Temperature.SetDisplayType(sType)
|
||||
StormFox2.Setting.Set("display_temperature",convert_to[sType] and sType or "celsius")
|
||||
if convert_to[sType] then
|
||||
return true
|
||||
end
|
||||
return sType == "celsius"
|
||||
end
|
||||
|
||||
---Returns the current display temperature type.
|
||||
---@return string
|
||||
---@client
|
||||
function StormFox2.Temperature.GetDisplayType()
|
||||
return temp_type
|
||||
end
|
||||
|
||||
---Converts the current (or given) temperature to the clients temperature-unit. Ideal for displays.
|
||||
---@param nCelcius number
|
||||
---@return number
|
||||
---@client
|
||||
function StormFox2.Temperature.GetDisplay(nCelcius)
|
||||
if nCelcius then
|
||||
return StormFox2.Temperature.Convert(nil,temp_type,nCelcius)
|
||||
end
|
||||
return StormFox2.Temperature.Get(temp_type)
|
||||
end
|
||||
|
||||
---Returns the clients temperature-unit symbol. ("°C", "°F" ..)
|
||||
---@return string
|
||||
---@client
|
||||
function StormFox2.Temperature.GetDisplaySymbol()
|
||||
return symbol[temp_type] or "°C"
|
||||
end
|
||||
|
||||
---Returns the default temperature, based on client-country.
|
||||
---@return string
|
||||
---@client
|
||||
function StormFox2.Temperature.GetDisplayDefault()
|
||||
return default_temp
|
||||
end
|
||||
-- Load the temperature settings.
|
||||
-- Setup setting
|
||||
StormFox2.Setting.AddCL("display_temperature",default_temp)
|
||||
local t = {}
|
||||
for k, v in pairs(symbol) do
|
||||
if t[k] then continue end
|
||||
t[k] = string.upper(k[1]) .. string.sub(k, 2) .. " " .. v
|
||||
end
|
||||
StormFox2.Setting.SetType( "display_temperature", t, {"celsius", "fahrenheit", "kelvin"} )
|
||||
StormFox2.Setting.Callback("display_temperature",function(sType)
|
||||
temp_type = convert_to[sType] and sType or "celsius"
|
||||
end,"StormFox2.temp.type")
|
||||
-- Load setting
|
||||
local sType = StormFox2.Setting.Get("display_temperature",default_temp)
|
||||
temp_type = convert_to[sType] and sType or "celsius"
|
||||
|
||||
hook.Remove("stormfox2.postlib", "StormFox2.TemperatureSettings")
|
||||
|
||||
---Sets the "local" temperature. This will override the server variable until called again with 'nil'.
|
||||
---@param nCelcius? number
|
||||
---@client
|
||||
function StormFox2.Temperature.SetLocal( nCelcius )
|
||||
tempOverwrite = nCelcius
|
||||
end
|
||||
|
||||
end
|
||||
535
lua/stormfox2/framework/sh_thunder.lua
Normal file
535
lua/stormfox2/framework/sh_thunder.lua
Normal file
@@ -0,0 +1,535 @@
|
||||
--[[
|
||||
| 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.Thunder = {}
|
||||
|
||||
---Returns true if it is thundering.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Thunder.IsThundering()
|
||||
return StormFox2.Data.Get("nThunder", 0) > 0
|
||||
end
|
||||
|
||||
---Returns the amount of posible strikes pr minute.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Thunder.GetActivity()
|
||||
return StormFox2.Data.Get("nThunder", 0)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local THUNDER_MAKE_SKYBOX = 0
|
||||
local THUNDER_TRACE_ERROR = 1
|
||||
local THUNDER_SUCCESS = 2
|
||||
|
||||
local function ETHull(pos,pos2,size,mask)
|
||||
local t = util.TraceHull( {
|
||||
start = pos,
|
||||
endpos = pos2,
|
||||
maxs = Vector(size,size,4),
|
||||
mins = Vector(-size,-size,0),
|
||||
mask = mask
|
||||
} )
|
||||
t.HitPos = t.HitPos or pos + pos2
|
||||
return t
|
||||
end
|
||||
-- Damages an entity
|
||||
local function StrikeDamageEnt( ent )
|
||||
if not ent or not IsValid(ent) then return end
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin( ent:GetPos() )
|
||||
effectdata:SetEntity(ent)
|
||||
effectdata:SetMagnitude(2)
|
||||
effectdata:SetScale(3)
|
||||
for i = 1,100 do
|
||||
util.Effect( "TeslaHitboxes", effectdata, true, true )
|
||||
end
|
||||
local ctd = DamageInfo()
|
||||
ctd:IsDamageType(DMG_SHOCK)
|
||||
ctd:SetDamage(math.Rand(90,200))
|
||||
local vr = VectorRand()
|
||||
vr.z = math.abs(vr.z)
|
||||
ctd:SetDamageForce(vr * 1000)
|
||||
ctd:SetInflictor(game.GetWorld())
|
||||
ctd:SetAttacker(game.GetWorld())
|
||||
ent:TakeDamageInfo(ctd)
|
||||
end
|
||||
-- Damanges an area
|
||||
local function StrikeDamagePos( vPos )
|
||||
local b_InWater = bit.band( util.PointContents( vPos ), CONTENTS_WATER ) == CONTENTS_WATER
|
||||
local t = {}
|
||||
for _,ent in ipairs(ents.FindInSphere(vPos,750)) do
|
||||
-- It hit in water, and you're nearby
|
||||
if b_InWater and ent:WaterLevel() > 0 then
|
||||
StrikeDamageEnt(ent)
|
||||
table.insert(t, ent)
|
||||
elseif ent:GetPos():Distance( vPos ) < 150 then
|
||||
StrikeDamageEnt(ent)
|
||||
table.insert(t, ent)
|
||||
end
|
||||
end
|
||||
hook.Run("StormFox2.Thunder.OnStrike", vPos, t)
|
||||
end
|
||||
-- Make STrike
|
||||
local Sway = 100
|
||||
|
||||
-- Traces a lightningstrike from sky to ground.
|
||||
|
||||
local BrushZ = math.max(1, ( StormFox2.Map.MaxSize().z - StormFox2.Map.MinSize().z ) / 20000)
|
||||
|
||||
local function MakeStrikeDown( vPos, nTraceSize )
|
||||
if not nTraceSize then nTraceSize = 512 end
|
||||
-- Find the sky position at the area
|
||||
local SkyPos = StormFox2.DownFall.FindSky( vPos, vector_up, 8 )
|
||||
if not SkyPos then -- Unable to find sky above. Get the higest point
|
||||
SkyPos = Vector(vPos.x, vPos.y, StormFox2.Map.MaxSize().z)
|
||||
SkyPos = StormFox2.DownFall.FindSky( SkyPos, vector_up, 1 ) or SkyPos
|
||||
end
|
||||
--debugoverlay.Box(SkyPos, Vector(1,1,1) * -20, Vector(1,1,1) * 20, 15, Color(255,0,0))
|
||||
-- Find the strike distance (Some maps are suuuper tall)
|
||||
local tr = ETHull(SkyPos - vector_up * 10, Vector( vPos.x, vPos.y, StormFox2.Map.MinSize().z), 256, MASK_SOLID_BRUSHONLY )
|
||||
if tr.AllSolid or tr.Fraction == 0 then -- This is inside solid and should instead be done in the skybox.
|
||||
return THUNDER_MAKE_SKYBOX, SkyPos
|
||||
end
|
||||
SkyPos.z = math.min( SkyPos.z, tr.HitPos.z + 8000 )
|
||||
local line = {}
|
||||
table.insert(line,pos)
|
||||
-- Create a line down
|
||||
local olddir = Vector(0,0,-1)
|
||||
local m_dis = math.max(1, ( SkyPos.z - StormFox2.Map.MinSize().z ) / 20000)
|
||||
local pos = SkyPos - vector_up * 5
|
||||
local _n = nTraceSize / 40
|
||||
for i = 20, 1, -1 do
|
||||
-- Random sway
|
||||
local randir = Angle(math.Rand(-Sway,Sway) + 90,math.random(360),math.Rand(-Sway,Sway)):Forward() + olddir
|
||||
randir:Normalize()
|
||||
randir.z = -math.abs(randir.z)
|
||||
olddir = randir
|
||||
local pos2 = pos + randir * math.Rand(800, 1000) * m_dis
|
||||
local m_dis = math.max(1, ( StormFox2.Map.MaxSize().z - StormFox2.Map.MinSize().z ) / 20000)
|
||||
local tr = ETHull(pos, pos2, nTraceSize - i * _n )
|
||||
--debugoverlay.Line(pos, pos2, 10)
|
||||
if not tr.Hit then
|
||||
table.insert(line, pos2)
|
||||
pos = pos2
|
||||
else
|
||||
if tr.HitSky then -- We hit the side of the skybox. Go to other way
|
||||
olddir = -olddir
|
||||
else
|
||||
table.insert(line, tr.HitPos)
|
||||
return THUNDER_SUCCESS, line, tr
|
||||
end
|
||||
end
|
||||
end
|
||||
return line, tr
|
||||
end
|
||||
|
||||
-- Creates a lightniingstrike up ( Useful when forcing a lightning strike to hit something )
|
||||
local function MakeStrikeUp( vPos )
|
||||
local SkyPos = StormFox2.DownFall.FindSky( vPos, vector_up, 8 )
|
||||
if not SkyPos then -- Unable to find sky above. Get the higest point
|
||||
SkyPos = Vector(vPos.x, vPos.y, StormFox2.Map.MaxSize().z)
|
||||
SkyPos = StormFox2.DownFall.FindSky( SkyPos, vector_up, 1 ) or SkyPos
|
||||
end
|
||||
local olddir = Vector(0,0,-1)
|
||||
local pos = vPos
|
||||
local m_dis = math.max(1, ( StormFox2.Map.MaxSize().z - StormFox2.Map.MinSize().z ) / 20000)
|
||||
local list = {pos}
|
||||
for i = 1, 20 do
|
||||
local randir = Angle(math.Rand(-Sway,Sway) + 90,math.random(360),math.Rand(-Sway,Sway)):Forward() + olddir
|
||||
randir:Normalize()
|
||||
randir.z = -math.abs(randir.z)
|
||||
olddir = randir
|
||||
local pos2 = pos + -randir * math.Rand(800, 1000) * m_dis
|
||||
if pos2.z >= SkyPos.z then
|
||||
table.insert(list, Vector(pos2.x, pos2.y, SkyPos.z))
|
||||
break
|
||||
else
|
||||
pos = pos2
|
||||
table.insert(list, pos)
|
||||
end
|
||||
end
|
||||
return table.Reverse(list)
|
||||
end
|
||||
|
||||
local function LightFluff(tList, b_InSkybox )
|
||||
local n_Length = math.Rand(.4,0.7)
|
||||
local n_Light = math.random(200, 250)
|
||||
|
||||
net.Start(StormFox2.Net.Thunder)
|
||||
net.WriteBool( true )
|
||||
net.WriteUInt(#tList, 5)
|
||||
for i = 1, #tList do
|
||||
net.WriteVector(tList[i])
|
||||
end
|
||||
net.WriteBool( b_InSkybox )
|
||||
net.WriteFloat(n_Length)
|
||||
net.WriteUInt(n_Light,8)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
---Creates a lightningstrike at a given position. Will return a hit entity as a second argument.
|
||||
---@param pos Vector
|
||||
---@return boolean success
|
||||
---@return Entity
|
||||
---@server
|
||||
function StormFox2.Thunder.CreateAt( pos )
|
||||
local t_Var, tList, tr
|
||||
local b_InSkybox = false
|
||||
local vMapMin = StormFox2.Map.MinSize()
|
||||
local vMapMax = StormFox2.Map.MaxSize()
|
||||
if not pos then
|
||||
pos = Vector( math.Rand(vMapMin.x, vMapMax.x), math.Rand(vMapMin.y, vMapMax.y), math.Rand(vMapMax.z, vMapMin.z / 2) )
|
||||
end
|
||||
local bInside = pos.x >= vMapMin.x and pos.x <= vMapMax.x and vMapMin.y and pos.y <= vMapMax.y
|
||||
if bInside then
|
||||
t_Var, tList, tr = MakeStrikeDown( pos )
|
||||
if t_Var == THUNDER_MAKE_SKYBOX then
|
||||
bInside = false
|
||||
end
|
||||
end
|
||||
if not bInside then -- Outside the map
|
||||
tList = MakeStrikeUp( Vector(pos.x, pos.y, StormFox2.Map.MinSize().z) )
|
||||
b_InSkybox = true
|
||||
end
|
||||
if not tList then return false end -- Unable to create lightning strike here.
|
||||
local hPos = tr and tr.HitPos or tList[#tList]
|
||||
if not hPos then return end
|
||||
if tr and IsValid( tr.Entity ) then
|
||||
table.insert(tList, tr.Entity:GetPos() + tr.Entity:OBBCenter())
|
||||
hPos = tr.Entity:GetPos() + tr.Entity:OBBCenter()
|
||||
end
|
||||
StrikeDamagePos( hPos )
|
||||
LightFluff(tList, b_InSkybox )
|
||||
return true, tr and IsValid( tr.Entity ) and tr.Entity
|
||||
end
|
||||
|
||||
---Creates a lightning strike to hit the given position / entity.
|
||||
---@param zPosOrEnt Vector|Entity
|
||||
---@param bRangeDamage number
|
||||
---@return boolean success
|
||||
---@server
|
||||
function StormFox2.Thunder.Strike( zPosOrEnt, bRangeDamage )
|
||||
-- Strike the entity
|
||||
if not bRangeDamage and zPosOrEnt.Health then
|
||||
local ent = zPosOrEnt
|
||||
timer.Simple(0.4, function()
|
||||
StrikeDamageEnt( ent )
|
||||
end)
|
||||
end
|
||||
if zPosOrEnt.GetPos then
|
||||
if zPosOrEnt.OBBCenter then
|
||||
zPosOrEnt = zPosOrEnt:GetPos() + zPosOrEnt:OBBCenter()
|
||||
else
|
||||
zPosOrEnt = zPosOrEnt:GetPos()
|
||||
end
|
||||
end
|
||||
if bRangeDamage then
|
||||
timer.Simple(0.4, function() StrikeDamagePos( zPosOrEnt ) end)
|
||||
end
|
||||
local b_InSkybox = not StormFox2.Map.IsInside( zPosOrEnt )
|
||||
--if b_InSkybox then
|
||||
-- zPosOrEnt = StormFox2.Map.WorldtoSkybox( zPosOrEnt )
|
||||
--end
|
||||
|
||||
local tList = MakeStrikeUp( zPosOrEnt )
|
||||
LightFluff(tList, false )
|
||||
return true
|
||||
end
|
||||
|
||||
---Creates a rumble.
|
||||
---@param pos Vector
|
||||
---@param bLight boolean
|
||||
---@server
|
||||
function StormFox2.Thunder.Rumble( pos, bLight )
|
||||
if not pos then
|
||||
local vMapMin = StormFox2.Map.MinSize()
|
||||
local vMapMax = StormFox2.Map.MaxSize()
|
||||
pos = Vector( math.Rand(vMapMin.x, vMapMax.x), math.Rand(vMapMin.y, vMapMax.y), math.Rand(vMapMax.z, vMapMin.z / 2) )
|
||||
end
|
||||
local n_Length = bLight and math.Rand(.4,0.7) or 0
|
||||
local n_Light = bLight and math.random(150, 250) or 0
|
||||
net.Start( StormFox2.Net.Thunder )
|
||||
net.WriteBool( false )
|
||||
net.WriteVector( pos )
|
||||
net.WriteFloat(n_Length)
|
||||
net.WriteUInt(n_Light,8)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
-- Enables thunder and makes them spawn at random, until set off or another weather gets selected
|
||||
local b = false
|
||||
local n = math.max(StormFox2.Map.MaxSize().x, StormFox2.Map.MaxSize().y, -StormFox2.Map.MinSize().x,-StormFox2.Map.MinSize().y)
|
||||
if StormFox2.Map.Has3DSkybox() then
|
||||
n = n * 1.5
|
||||
end
|
||||
|
||||
do
|
||||
local n = 0
|
||||
---Enables / Disables thunder.
|
||||
---@param bEnable boolean
|
||||
---@param nActivityPrMinute? number
|
||||
---@param nTimeAmount? number
|
||||
---@server
|
||||
function StormFox2.Thunder.SetEnabled( bEnable, nActivityPrMinute, nTimeAmount )
|
||||
n = 0
|
||||
if bEnable then
|
||||
StormFox2.Network.Set("nThunder", nActivityPrMinute)
|
||||
if nTimeAmount then
|
||||
StormFox2.Network.Set("nThunder", 0, nTimeAmount)
|
||||
end
|
||||
else
|
||||
StormFox2.Network.Set("nThunder", 0)
|
||||
end
|
||||
end
|
||||
hook.Add("Think", "StormFox2.thunder.activity", function()
|
||||
if not StormFox2.Thunder.IsThundering() then return end
|
||||
if n >= CurTime() then return end
|
||||
local a = StormFox2.Thunder.GetActivity()
|
||||
n = CurTime() + math.random(50, 60) / a
|
||||
-- Strike or rumble
|
||||
if math.random(1,3) < 3 then
|
||||
StormFox2.Thunder.CreateAt()
|
||||
else
|
||||
StormFox2.Thunder.Rumble( nil, math.random(10) > 1 )
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
lightningStrikes = lightningStrikes or {}
|
||||
local _Light, _Stop, _Length = 0,0,0
|
||||
|
||||
---Returns light created by thunder.
|
||||
---@return number
|
||||
---@client
|
||||
function StormFox2.Thunder.GetLight()
|
||||
if _Light <= 0 then return 0 end
|
||||
if _Stop < CurTime() then
|
||||
_Light = 0
|
||||
return 0
|
||||
end
|
||||
local t = (_Stop - CurTime()) / _Length
|
||||
local c = math.abs(math.sin( t * math.pi ))
|
||||
local l = _Light * c
|
||||
return math.random(l, l * 0.5) -- Flicker a bit
|
||||
end
|
||||
|
||||
-- 0 - 2000
|
||||
local CloseStrikes = {"sound/stormfox2/amb/thunder_strike.ogg"}
|
||||
-- 2000 - 20000
|
||||
local MediumStrikes = {"sound/stormfox2/amb/thunder_strike.ogg", "sound/stormfox2/amb/thunder_strike2.ogg"}
|
||||
-- 20000 +
|
||||
local FarStrikes = {}
|
||||
|
||||
if IsMounted("csgo") or IsMounted("left4dead2") then
|
||||
table.insert(FarStrikes, "ambient/weather/thunderstorm/lightning_strike_1.wav")
|
||||
table.insert(FarStrikes, "ambient/weather/thunderstorm/lightning_strike_4.wav")
|
||||
end
|
||||
if #FarStrikes < 1 then
|
||||
table.insert(FarStrikes, "sound/stormfox2/amb/thunder_strike2.ogg")
|
||||
end
|
||||
|
||||
local snd_buffer = {}
|
||||
local function StrikeEffect( pos, n_Length )
|
||||
local dlight = DynamicLight( 1 )
|
||||
if ( dlight ) then
|
||||
dlight.pos = pos
|
||||
dlight.r = 255
|
||||
dlight.g = 255
|
||||
dlight.b = 255
|
||||
dlight.brightness = 6
|
||||
dlight.Decay = 3000 / n_Length
|
||||
dlight.Size = 256 * 8
|
||||
dlight.DieTime = CurTime() + n_Length
|
||||
end
|
||||
local effectdata = EffectData()
|
||||
local s = math.random(5, 8)
|
||||
effectdata:SetOrigin( pos + vector_up * 4 )
|
||||
effectdata:SetMagnitude( s / 2 )
|
||||
effectdata:SetNormal(vector_up)
|
||||
effectdata:SetRadius( 8 )
|
||||
util.Effect( "Sparks", effectdata, true, true )
|
||||
end
|
||||
|
||||
local function PlayStrike( vPos, nViewDis, viewPos )
|
||||
local snd = ""
|
||||
if nViewDis <= 2000 then
|
||||
snd = table.Random(CloseStrikes)
|
||||
elseif nViewDis <= 15000 then
|
||||
snd = table.Random(MediumStrikes)
|
||||
else
|
||||
snd = table.Random(FarStrikes)
|
||||
end
|
||||
if string.sub(snd, 0, 6 ) == "sound/" or string.sub(snd,-4) == ".ogg" then
|
||||
sound.PlayFile( snd, "3dnoplay", function( station, errCode, errStr )
|
||||
if ( IsValid( station ) ) then
|
||||
station:Set3DFadeDistance( 0, 10 )
|
||||
station:SetVolume( 1)
|
||||
station:SetPos(vPos)
|
||||
station:Play()
|
||||
end
|
||||
end)
|
||||
else
|
||||
surface.PlaySound( snd )
|
||||
end
|
||||
end
|
||||
--[[
|
||||
Sound moves about 343 meters pr second
|
||||
52.49 hU = 1 meter ~ 18.004 hU pr second
|
||||
]]
|
||||
local b = true
|
||||
local function SndThink()
|
||||
if #snd_buffer < 1 then
|
||||
hook.Remove("Think","StormFox2.Thunder.SndDis")
|
||||
b = false
|
||||
return
|
||||
end
|
||||
local r = {}
|
||||
local view = StormFox2.util.ViewEntity():GetPos()
|
||||
local c = CurTime() - 0.2
|
||||
for k,v in ipairs( snd_buffer ) do
|
||||
local travel = (c - v[2]) * 18004
|
||||
local vDis = view:Distance( v[1] )
|
||||
if vDis - travel < 0 then
|
||||
table.insert(r, k)
|
||||
PlayStrike( v[1], vDis, view )
|
||||
end
|
||||
end
|
||||
for i = #r, 1, -1 do
|
||||
table.remove(snd_buffer, r[i])
|
||||
end
|
||||
end
|
||||
hook.Add("Think","StormFox2.Thunder.SndDis", SndThink)
|
||||
|
||||
local function Strike( tList, b_InSkybox, n_Length, n_Light )
|
||||
table.insert(lightningStrikes, {CurTime() + n_Length, n_Length, b_InSkybox, tList, true})
|
||||
local pos = tList[#tList][1]
|
||||
sound.Play("ambient/energy/weld" .. math.random(1,2) .. ".wav", pos)
|
||||
if not b then
|
||||
hook.Add("Think","StormFox2.Thunder.SndDis", SndThink)
|
||||
end
|
||||
table.insert(snd_buffer, {pos, CurTime()})
|
||||
local c = CurTime()
|
||||
_Light = 255
|
||||
_Length = .7
|
||||
_Stop = math.max(c + _Length, _Stop)
|
||||
end
|
||||
|
||||
local function Rumble( vPos, n_Length, n_Light )
|
||||
-- Thunder is at 120dB
|
||||
local c = CurTime()
|
||||
_Light = n_Light
|
||||
_Length = n_Length
|
||||
_Stop = math.max(c + n_Length, _Stop)
|
||||
sound.Play("ambient/atmosphere/thunder" .. math.random(3,4) .. ".wav", StormFox2.util.ViewEntity():GetPos(), 150)
|
||||
|
||||
end
|
||||
local Sway = 120
|
||||
net.Receive( StormFox2.Net.Thunder, function(len)
|
||||
local b_Strike = net.ReadBool()
|
||||
if b_Strike then
|
||||
local tList = {}
|
||||
local old
|
||||
local n = net.ReadUInt(5)
|
||||
for i = 1, n do
|
||||
local randir = Angle(math.Rand(-Sway,Sway) + 90,math.random(360),math.Rand(-Sway,Sway))
|
||||
local new = net.ReadVector()
|
||||
if old then
|
||||
randir = randir:Forward() + (new - old):Angle():Forward() * 2
|
||||
else
|
||||
randir = randir:Forward()
|
||||
end
|
||||
old = new
|
||||
tList[i] = {new,math.Rand(1.2,1.5),randir,math.random(0,1)}
|
||||
--debugoverlay.Sphere(new, 15, 15, Color(255,255,255), true)
|
||||
end
|
||||
local b_InSkybox = net.ReadBool()
|
||||
Strike(tList, b_InSkybox, net.ReadFloat(), net.ReadUInt(8))
|
||||
else
|
||||
local vPos = net.ReadVector()
|
||||
Rumble(vPos, net.ReadFloat(), net.ReadUInt(8) )
|
||||
end
|
||||
end)
|
||||
|
||||
-- Render Strikes
|
||||
local tex = {(Material("stormfox2/effects/lightning"))}
|
||||
local texend = {(Material("stormfox2/effects/lightning_end")),(Material("stormfox2/effects/lightning_end2"))}
|
||||
for k, v in ipairs( texend ) do
|
||||
v:SetFloat("$nofog",1)
|
||||
end
|
||||
local t = 0
|
||||
hook.Add("PostDrawOpaqueRenderables","StormFox2.Render.Lightning",function(a,sky)
|
||||
if a or #lightningStrikes < 1 then return end
|
||||
if sky then return end -- Doesn't work yet
|
||||
local r = {}
|
||||
local c = CurTime()
|
||||
local col = Color( 255, 255, 255, 255)
|
||||
for k, v in ipairs( lightningStrikes ) do
|
||||
-- Render world or skybox
|
||||
--if v[3] ~= sky then continue end
|
||||
-- Remove if dead
|
||||
if v[1] < c then
|
||||
table.insert(r, k)
|
||||
continue
|
||||
end
|
||||
|
||||
local life = 1 - ( v[1] - c ) / v[2] -- 0 - 1
|
||||
if life < 0.6 then
|
||||
col.a = 425 * life
|
||||
else
|
||||
col.a = 637.5 * (1 - life)
|
||||
end
|
||||
local fuzzy = life < 0.6
|
||||
local i = 0.6 / #v[4]
|
||||
if v[5] and not fuzzy then
|
||||
StrikeEffect( v[4][#v[4]][1] , life )
|
||||
lightningStrikes[k][5] = false
|
||||
end
|
||||
-- Render beams
|
||||
render.SetMaterial(tex[1])
|
||||
render.StartBeam(#v[4])
|
||||
local l = math.Rand(0.4, 0.8)
|
||||
for k2, v2 in ipairs( v[4] ) do
|
||||
if life < k2 * i then break end
|
||||
local tp = 0.1
|
||||
render.AddBeam( v2[1], 400, (l * (k2 - 1)) % 1, col )
|
||||
end
|
||||
render.EndBeam()
|
||||
|
||||
-- Render strikes
|
||||
if fuzzy then
|
||||
for k2, v2 in ipairs( v[4] ) do
|
||||
if life < k2 * i or k2 + 3 >= #v[4] then break end
|
||||
local n2 = life * 2- k2 * 0.04
|
||||
local vec = v2[1]
|
||||
local tp = 1 / #v[4] * k2
|
||||
local n = k2 % #texend + 1
|
||||
render.SetMaterial(texend[n])
|
||||
local w,h = texend[n]:Width() * n2,texend[n]:Height() * n2
|
||||
render.DrawBeam( vec, vec + v2[3] * h * v2[2], w * v2[2], 1 - n2, 1, col )
|
||||
end
|
||||
else
|
||||
local v1 = v[4][1][1]
|
||||
local v2 = v[4][#v[4]][1]
|
||||
local vc = (v1 + v2) / 2
|
||||
vc.z = v2.z
|
||||
local vc2 = Vector(vc.x,vc.y,v1.z)
|
||||
local a = math.max(0, 1 - life - 0.2)
|
||||
col.a = a * 1275
|
||||
render.SetMaterial(Material("stormfox2/effects/lightning_light"))
|
||||
render.DrawBeam(vc, vc2, 24400, 0.3 , 0.7, col)
|
||||
end
|
||||
end
|
||||
for i = #r, 1, -1 do
|
||||
table.remove(lightningStrikes, r[i])
|
||||
end
|
||||
end)
|
||||
end
|
||||
779
lua/stormfox2/framework/sh_timemaster.lua
Normal file
779
lua/stormfox2/framework/sh_timemaster.lua
Normal file
@@ -0,0 +1,779 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
My god, look at the time.
|
||||
|
||||
StormFox2.Time.GetTime(bNearestSecond) [nTime] -- Returns the time number. Between 0 and 1440
|
||||
StormFox2.Time.TimeToString(nTime = current time,bUse12Hour) [sTime] -- Returns the time as a string.
|
||||
StormFox2.Time.IsDay() [bIsDay] -- Returns true if its day
|
||||
StormFox2.Time.IsNight() [bIsNight] -- Returns true if its night
|
||||
StormFox2.Time.GetStamp(nTime = current time) [nTimeStamp] -- Returns a timestamp.
|
||||
|
||||
SERVER
|
||||
StormFox2.Time.Set(nTime or string) -- Sets the time. Also supports a string "12:00" or "5:00 AM".
|
||||
StormFox2.Time.SetSpeed(nSpeed) -- Sets the timespeed.
|
||||
|
||||
Hooks:
|
||||
StormFox2.Time.Set -- Called when the time gets set.
|
||||
StormFox2.Time.NewStamp NewStamp OldStamp -- Callend when the time matches a new stamp
|
||||
|
||||
BASE_TIME is CurTime + StartTime
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
---A number between 0 and 1440. Where 720 is midday and 0 / 1440 is midnight.
|
||||
---@class TimeNumber : number
|
||||
|
||||
local floor,ceil,random = math.floor, math.ceil, math.random
|
||||
StormFox2.Time = StormFox2.Time or {}
|
||||
-- Settings
|
||||
local s_start = StormFox2.Setting.AddSV("start_time",-1,nil, "Time", -1, 1440) -- Sets the starttime
|
||||
local s_real = StormFox2.Setting.AddSV("real_time",false,nil, "Time") -- Sets the startime to match OS
|
||||
local s_random = StormFox2.Setting.AddSV("random_time",false,nil,"Time") -- Makes the time random
|
||||
local s_continue = StormFox2.Setting.AddSV("continue_time",true,nil,"Time"):SetRadioDefault() -- Make the time continue from last
|
||||
|
||||
s_start:SetRadioAll( s_real, s_random, s_continue )
|
||||
|
||||
StormFox2.Setting.SetType("start_time","Time_toggle")
|
||||
|
||||
local day_length = StormFox2.Setting.AddSV("day_length", 12,nil,"Time",-1, 24 * 60 * 7 )
|
||||
:SetMenuType("special_float")
|
||||
|
||||
local night_length = StormFox2.Setting.AddSV("night_length", 12,nil,"Time",-1, 24 * 60 * 7 )
|
||||
:SetMenuType("special_float")
|
||||
|
||||
local sun_rise = StormFox2.Setting.AddSV("sunrise",360,nil, "Time", 0, 1440)
|
||||
StormFox2.Setting.SetType("sunrise", "Time")
|
||||
local sun_set = StormFox2.Setting.AddSV("sunset",1080,nil, "Time", 0, 1440)
|
||||
StormFox2.Setting.SetType("sunset", "Time")
|
||||
|
||||
--[[
|
||||
Pause
|
||||
day_length = <= 0
|
||||
night_length = <= 0
|
||||
Only day
|
||||
day_length = > 0
|
||||
night_length = < 0
|
||||
Only night
|
||||
day_length = < 0
|
||||
night_length = >= 0
|
||||
]]
|
||||
|
||||
|
||||
--[[ ---- EDIT NOTE ----
|
||||
x Instead of using Settings directly in UpdateMath. MAke UpdateMath use arguments instead.
|
||||
|
||||
These settings are send from the server to client on SetTime or join-data.
|
||||
|
||||
When changing settings on the server, wait a few ticks to set them. Sometimes there are multiple settings being changed at the same time.
|
||||
Best to wait a bit.
|
||||
|
||||
StartTime also got removed .. need to fix that.
|
||||
]]
|
||||
|
||||
-- Returns the total time in minutes for a day
|
||||
local BASE_TIME = 0
|
||||
--[[
|
||||
Calculates the regular time
|
||||
cycleTime - The total time it takes for a day to pass
|
||||
|
||||
Enums to keep me sane
|
||||
- finishTime = The finished number between 0 and 1440. This is the ingame time
|
||||
- cycleTime = The total seconds it takes for a day to pass
|
||||
- dayTime = The total seconds it takes for "day-light" to pass
|
||||
- nightTime = The total seconds it takes for a night to pass
|
||||
- sunTime = The total of ingame the sun is up
|
||||
- nightTime = The total of ingame the sun is down
|
||||
- cyclePercentDay= The percent of the day, being day-light (Only with day and night on)
|
||||
]]
|
||||
-- Math Box to set and get time
|
||||
local Get, Set, UpdateMath, isInDay, isDay, dayLength, nightLength, netWriteData
|
||||
local GetCache, IsDayCache, CycleCache, FinsihToCycle, GetCycleRaw
|
||||
local CR
|
||||
do
|
||||
local SF_PAUSE = 0
|
||||
local SF_NORMAL = 1
|
||||
local SF_DAYONLY = 2
|
||||
local SF_NIGHTONLY = 3
|
||||
local SF_REAL = 4
|
||||
|
||||
local cycleLength
|
||||
local curType -- The time-type
|
||||
-- Returns the percent from the given time ( 0 - 1440) between starttime and endtime
|
||||
-- Also loops around if from is higer than to
|
||||
local function lerp1440( time, from, to )
|
||||
if from < to then
|
||||
return ( time - from ) / ( to - from )
|
||||
elseif time >= from then
|
||||
return ( time - from ) / ( 1440 - from + to )
|
||||
else
|
||||
local ex = 1440 - from
|
||||
return ( time + ex ) / ( to + ex )
|
||||
end
|
||||
end
|
||||
local sunTimeUp, nightTimeUp, sunSet, sunRise
|
||||
function isInDay( finishTime )
|
||||
if not sunRise then return true end -- Not loaded yet
|
||||
if sunRise < sunSet then
|
||||
return finishTime >= sunRise and finishTime <= sunSet
|
||||
end
|
||||
return (finishTime >= sunRise and finishTime <= 1440 ) or finishTime <= sunSet
|
||||
end
|
||||
-- Splits cycletime into dayPercent and nightPercent
|
||||
local function CycleToPercent( cycleTime )
|
||||
if cycleTime <= dayLength then -- It is day
|
||||
return cycleTime / dayLength, nil
|
||||
else -- It is night
|
||||
return nil, (cycleTime - dayLength) / nightLength
|
||||
end
|
||||
end
|
||||
-- Takes dayPercent or nightPercent and convert it to cycletime
|
||||
local function PercentToCycle( dayPercent, nightPercent )
|
||||
if dayPercent then
|
||||
return dayPercent * dayLength
|
||||
else
|
||||
return dayLength + nightLength * nightPercent
|
||||
end
|
||||
end
|
||||
-- returns percent of the day that has passed at the given time
|
||||
local function finishDayToPercent( finishTime )
|
||||
return lerp1440( finishTime, sunRise, sunSet )
|
||||
end
|
||||
-- returns percent of the night that has passed at the given time
|
||||
local function finishNightToPercent( finishTime )
|
||||
return lerp1440( finishTime, sunSet, sunRise )
|
||||
end
|
||||
-- Takes the ingame 0-1440 and converts it to the cycle-area
|
||||
function FinsihToCycle( finishTime )
|
||||
if isInDay( finishTime ) then -- If day
|
||||
return finishDayToPercent( finishTime ) * dayLength
|
||||
else
|
||||
return dayLength + finishNightToPercent( finishTime ) * nightLength
|
||||
end
|
||||
end
|
||||
local function CycleToFinish( cycle )
|
||||
if cycle <= dayLength then -- Day time
|
||||
local percent = cycle / dayLength
|
||||
return ( sunRise + sunTimeUp * percent ) % 1440
|
||||
else -- NightTime
|
||||
local percent = ( cycle - dayLength ) / nightLength
|
||||
return ( sunSet + nightTimeUp * percent ) % 1440
|
||||
end
|
||||
end
|
||||
-- Get
|
||||
local function TimeFromSettings( )
|
||||
-- The seconds passed in the day
|
||||
local chunk = ((CurTime() - BASE_TIME) % cycleLength)
|
||||
return CycleToFinish( chunk )
|
||||
end
|
||||
local function TimeFromSettings_DAY( )
|
||||
local p_chunk = ((CurTime() - BASE_TIME) % cycleLength) / cycleLength
|
||||
return (sunRise + p_chunk * sunTimeUp) % 1440
|
||||
end
|
||||
local function TimeFromSettings_NIGHT( )
|
||||
local p_chunk = ((CurTime() - BASE_TIME) % cycleLength) / cycleLength
|
||||
return (sunSet + p_chunk * nightTimeUp) % 1440
|
||||
end
|
||||
local function TimeFromPause()
|
||||
return BASE_TIME
|
||||
end
|
||||
-- Is cheaper than converting things around
|
||||
function isDay()
|
||||
if not cycleLength then return true end -- Not loaded yet
|
||||
if curType == SF_NIGHTONLY then
|
||||
return false
|
||||
elseif curType == SF_DAYONLY then
|
||||
return true
|
||||
else
|
||||
local l = (CurTime() - BASE_TIME) % cycleLength
|
||||
return l <= dayLength
|
||||
end
|
||||
end
|
||||
function Get()
|
||||
if not cycleLength then return 720 end -- Not loaded yet
|
||||
local num
|
||||
if not curType or curType == SF_NORMAL then
|
||||
num = TimeFromSettings( )
|
||||
GetCache = num
|
||||
IsDayCache = isDay()
|
||||
elseif curType == SF_REAL then
|
||||
num = ( CurTime() / 60 - BASE_TIME ) % 1440
|
||||
GetCache = num
|
||||
IsDayCache = isInDay( num )
|
||||
elseif curType == SF_PAUSE then
|
||||
num = TimeFromPause( )
|
||||
GetCache = num
|
||||
IsDayCache = isInDay( num )
|
||||
elseif curType == SF_DAYONLY then
|
||||
num = TimeFromSettings_DAY( )
|
||||
GetCache = num
|
||||
IsDayCache = true
|
||||
else
|
||||
num = TimeFromSettings_NIGHT( )
|
||||
GetCache = num
|
||||
IsDayCache = false
|
||||
end
|
||||
|
||||
return num
|
||||
end
|
||||
function Set( snTime )
|
||||
if not curType or curType == SF_NORMAL then
|
||||
BASE_TIME = CurTime() - FinsihToCycle( snTime )
|
||||
elseif curType == SF_REAL then
|
||||
BASE_TIME = CurTime() / 60 - snTime
|
||||
elseif curType == SF_PAUSE then
|
||||
BASE_TIME = snTime
|
||||
-- If you pause the time, we should save it if we got s_continue on.
|
||||
if SERVER and StormFox2.Loaded and s_continue:GetValue() then
|
||||
cookie.Set("sf2_lasttime", tostring(snTime))
|
||||
end
|
||||
elseif curType == SF_DAYONLY then
|
||||
local p = math.Clamp(lerp1440( snTime, sunRise, sunSet ), 0, 1)
|
||||
BASE_TIME = CurTime() - p * dayLength
|
||||
elseif curType == SF_NIGHTONLY then
|
||||
local p = math.Clamp(lerp1440( snTime, sunSet, sunRise ), 0, 1)
|
||||
BASE_TIME = CurTime() - p * nightLength
|
||||
end
|
||||
GetCache = nil -- Delete time cache
|
||||
-- Gets called when the user changes the time, or time variables. Tells scripts to recalculate things.
|
||||
if not StormFox2.Loaded then return end
|
||||
hook.Run("StormFox2.Time.Changed")
|
||||
end
|
||||
function UpdateMath(nsTime, blockSetTime)
|
||||
local nsTime = nsTime or ( cycleLength and Get() )
|
||||
sunSet = sun_set:GetValue()
|
||||
sunRise = sun_rise:GetValue()
|
||||
dayLength = day_length:GetValue() * 60
|
||||
nightLength = night_length:GetValue() * 60
|
||||
--print(sunSet)
|
||||
--print(sunRise)
|
||||
--print(dayLength)
|
||||
--print(nightLength)
|
||||
if s_real:GetValue() then -- Real time
|
||||
cycleLength = 60 * 60 * 24
|
||||
curType = SF_REAL
|
||||
elseif dayLength <= 0 and nightLength <= 0 or sunSet == sunRise then -- Pause type
|
||||
curType = SF_PAUSE
|
||||
cycleLength = 0
|
||||
elseif nightLength <= 0 then -- Day only
|
||||
cycleLength = dayLength
|
||||
curType = SF_DAYONLY
|
||||
elseif dayLength <= 0 then -- Night only
|
||||
cycleLength = nightLength
|
||||
curType = SF_NIGHTONLY
|
||||
else
|
||||
cycleLength = dayLength + nightLength
|
||||
curType = SF_NORMAL
|
||||
end
|
||||
if sunRise < sunSet then
|
||||
sunTimeUp = sunSet - sunRise
|
||||
else
|
||||
sunTimeUp = (1440 - sunRise) + sunSet
|
||||
end
|
||||
nightTimeUp = 1440 - sunTimeUp
|
||||
if not nsTime or blockSetTime then return end -- No valid time currently
|
||||
Set( nsTime )
|
||||
if SERVER then
|
||||
net.Start( StormFox2.Net.Time )
|
||||
net.WriteString( tostring( BASE_TIME ) )
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
local function GetDayPercent()
|
||||
if not IsDayCache then return -1 end
|
||||
local chunk = ((CurTime() - BASE_TIME) % cycleLength)
|
||||
return chunk / dayLength
|
||||
end
|
||||
local function GetNightPercent()
|
||||
if IsDayCache then return -1 end
|
||||
local chunk = ((CurTime() - BASE_TIME) % cycleLength)
|
||||
return (chunk - dayLength) / nightLength
|
||||
end
|
||||
function GetCycleRaw()
|
||||
if not cycleLength then return 0 end -- Not loaded, or pause on launch
|
||||
if CR then return CR end
|
||||
CR = ((CurTime() - BASE_TIME) % cycleLength)
|
||||
return CR
|
||||
end
|
||||
-- Returns how far the day has progressed 0 = sunRise, 0.5 = sunSet, 1 = sunRise
|
||||
function StormFox2.Time.GetCycleTime()
|
||||
if CycleCache then return CycleCache end
|
||||
if curType == SF_REAL then
|
||||
local t = Get()
|
||||
if isInDay( t ) then
|
||||
CycleCache = lerp1440( t, sunRise, sunSet ) / 2
|
||||
else
|
||||
CycleCache = 0.5 + lerp1440( t, sunSet, sunRise ) / 2
|
||||
end
|
||||
return CycleCache
|
||||
end
|
||||
if curType == SF_PAUSE then -- When paused, use the time to calculate
|
||||
if isInDay( BASE_TIME ) then
|
||||
CycleCache = lerp1440( BASE_TIME, sunRise, sunSet ) / 2
|
||||
else
|
||||
CycleCache = 0.5 + lerp1440( BASE_TIME, sunSet, sunRise ) / 2
|
||||
end
|
||||
return math.Clamp(CycleCache, 0, 1)
|
||||
end
|
||||
if IsDayCache then
|
||||
CycleCache = GetDayPercent() / 2
|
||||
return math.Clamp(CycleCache, 0, 1)
|
||||
elseif cycleLength then
|
||||
CycleCache = GetNightPercent() / 2 + 0.5
|
||||
return math.Clamp(CycleCache, 0, 1)
|
||||
else -- Idk
|
||||
return 0.5
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Cache clear. Wait 4 frames to update the time-cache, calculating it for every function is too costly.
|
||||
do
|
||||
local i = 0
|
||||
hook.Add("Think", "StormFox2.Time.ClearCache", function()
|
||||
CR = nil
|
||||
i = i + 1
|
||||
if i >= 2 then
|
||||
i = 0
|
||||
GetCache = nil
|
||||
CycleCache = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- In most cases, multiple settings will update at the same time. Wait a second.
|
||||
local function updateTimeSettings( )
|
||||
if timer.Exists("SF_SETTIME") then return end
|
||||
timer.Create("SF_SETTIME", 0.2, 1, function()
|
||||
UpdateMath( nil, CLIENT ) -- If we're the client, then don't update the BASE_TIME
|
||||
end)
|
||||
end
|
||||
-- If any of the settings change, update the math behind it. This will also fix time and update clients if done on server.
|
||||
day_length:AddCallback( updateTimeSettings,"SF_TIMEUPDATE")
|
||||
night_length:AddCallback( updateTimeSettings,"SF_TIMEUPDATE")
|
||||
sun_rise:AddCallback( updateTimeSettings,"SF_TIMEUPDATE")
|
||||
sun_set:AddCallback( updateTimeSettings,"SF_TIMEUPDATE")
|
||||
s_real:AddCallback( updateTimeSettings,"SF_TIMEUPDATE")
|
||||
|
||||
-- Make real-time change day and night length
|
||||
if SERVER then
|
||||
s_real:AddCallback( function( b )
|
||||
if not b then return end
|
||||
local dt = string.Explode(":",os.date("%H:%M:%S"))
|
||||
nsTime = tonumber(dt[1]) * 60 + tonumber(dt[2]) + tonumber(dt[3]) / 60
|
||||
StormFox2.Time.Set(nsTime)
|
||||
end,"SF_REALTIME_S")
|
||||
end
|
||||
|
||||
|
||||
-- Update the math within Get and Set. Will also try and adjust the time
|
||||
if SERVER then -- Server controls the time
|
||||
local start_time = math.Clamp(cookie.GetNumber("sf2_lasttime",-1) or -1, -1, 1439)
|
||||
if s_continue:GetValue() and start_time >= 0 then
|
||||
-- Continue time from last
|
||||
else
|
||||
if s_start:GetValue() >= 0 then -- Start time is on
|
||||
start_time = s_start:GetValue()
|
||||
elseif s_real:GetValue() then -- Real time
|
||||
local dt = string.Explode(":",os.date("%H:%M:%S"))
|
||||
start_time = tonumber(dt[1]) * 60 + tonumber(dt[2]) + tonumber(dt[3]) / 60
|
||||
else -- if s_random:GetValue() or start_time < 0 then Make it random if all options are invalid
|
||||
start_time = math.Rand(0, 1400)
|
||||
end
|
||||
end
|
||||
UpdateMath( start_time, false )
|
||||
|
||||
---Sets the time. TimeNumber is a number between 0 and 1440.
|
||||
---@param nsTime TimeNumber
|
||||
---@return boolean success
|
||||
---@see TimeNumber
|
||||
---@server
|
||||
function StormFox2.Time.Set( nsTime )
|
||||
if nsTime and type( nsTime ) == "string" then
|
||||
nsTime = StormFox2.Time.StringToTime(nsTime)
|
||||
end
|
||||
if not nsTime then return false end
|
||||
Set( nsTime )
|
||||
net.Start( StormFox2.Net.Time )
|
||||
net.WriteString( tostring( BASE_TIME ) ) -- Sending the current time might add a delay to clients. Better to send the new base.
|
||||
net.Broadcast()
|
||||
return true
|
||||
end
|
||||
-- Tell new clients the settings
|
||||
hook.Add("StormFox2.data.initspawn", "StormFox2.Time.SendOnJoin", function( ply )
|
||||
net.Start( StormFox2.Net.Time )
|
||||
net.WriteString( tostring( BASE_TIME ) )
|
||||
net.Send( ply )
|
||||
end)
|
||||
else
|
||||
UpdateMath( 720, true ) -- Set the starting time to 720. We don't know any settings yet.
|
||||
net.Receive( StormFox2.Net.Time, function(len)
|
||||
BASE_TIME = tonumber( net.ReadString() ) or 0
|
||||
end)
|
||||
end
|
||||
|
||||
---Returns the current time. TimeNumber is a number between 0 and 1440.
|
||||
---@param bNearestSecond boolean
|
||||
---@return TimeNumber
|
||||
---@shared
|
||||
function StormFox2.Time.Get( bNearestSecond )
|
||||
if bNearestSecond then
|
||||
return math.floor(GetCache and GetCache or Get())
|
||||
end
|
||||
return GetCache and GetCache or Get()
|
||||
end
|
||||
|
||||
---Returns the current timespeed / 60. Used for internal calculations.
|
||||
---@deprecated
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetSpeed_RAW()
|
||||
if not nightLength or StormFox2.Time.IsPaused() then return 0 end
|
||||
if IsDayCache then
|
||||
return 1 / dayLength
|
||||
end
|
||||
return 1 / nightLength
|
||||
end
|
||||
|
||||
---Returns the current timespeed. "How many seconds pr real second".
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetSpeed()
|
||||
return StormFox2.Time.GetSpeed_RAW() * 60
|
||||
end
|
||||
|
||||
-- Be able to load time
|
||||
local function thinkingBox(sVar) -- Converts string to something useful
|
||||
local h,m = string.match(sVar,"(%d?%d):?(%d?%d)")
|
||||
local ampm = string.match(sVar,"[ampAMP]+") or ""
|
||||
if not h or not m then return end
|
||||
if #ampm > 0 then
|
||||
if tonumber(h) > 12 then ampm = "" end
|
||||
end
|
||||
if #ampm < 1 then ampm = "" end
|
||||
return h .. ":" .. m .. " " .. ampm
|
||||
end
|
||||
|
||||
---Returns the given time as a number. Supports both "13:00" and "1:00 PM"
|
||||
---@param sTime string
|
||||
---@return TimeNumber|string
|
||||
---@shared
|
||||
function StormFox2.Time.StringToTime(sTime)
|
||||
sTime = sTime or StormFox2.Time.Get()
|
||||
str = thinkingBox(sTime)
|
||||
if not str then return end
|
||||
local a = string.Explode( ":", str )
|
||||
if #a < 2 then return end
|
||||
local h,m = string.match(a[1],"%d+"),string.match(a[2],"%d+")
|
||||
local ex = string.match(a[2]:lower(),"[amp]+")
|
||||
if not h or not m then return end
|
||||
h,m = tonumber(h),tonumber(m)
|
||||
if ex then
|
||||
-- 12clock to 24clock
|
||||
if ex == "am" and h == 12 then
|
||||
h = h - 12
|
||||
end
|
||||
if h < 12 and ex == "pm" then
|
||||
h = h + 12
|
||||
end
|
||||
end
|
||||
return ( h * 60 + m ) % 1440
|
||||
end
|
||||
|
||||
---A syncronised number used by the client to calculate the time. Use instead StormFox2.Time.Get to get the current time.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetBASE_TIME()
|
||||
return BASE_TIME
|
||||
end
|
||||
|
||||
---Returns the given or current time in a string format. Will use client setting if bUse12Hour is nil.
|
||||
---@param nTime? TimeNumber
|
||||
---@param bUse12Hour? boolean
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Time.TimeToString(nTime,bUse12Hour)
|
||||
if CLIENT and bUse12Hour == nil then
|
||||
bUse12Hour = StormFox2.Setting.GetCache("12h_display")
|
||||
end
|
||||
if not nTime then nTime = StormFox2.Time.Get(true) end
|
||||
local h = floor(nTime / 60)
|
||||
local m = floor(nTime % 60 )
|
||||
if not bUse12Hour then return h .. ":" .. (m < 10 and "0" or "") .. m end
|
||||
local e = "PM"
|
||||
if h < 12 or h == 0 then
|
||||
e = "AM"
|
||||
end
|
||||
if h == 0 then
|
||||
h = 12
|
||||
elseif h > 12 then
|
||||
h = h - 12
|
||||
end
|
||||
return h .. ":" .. (m < 10 and "0" or "") .. m .. " " .. e
|
||||
end
|
||||
-- Easy functions
|
||||
|
||||
---Returns true if the current or given time is doing the day.
|
||||
---@param nsTime? TimeNumber
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Time.IsDay( nsTime )
|
||||
if not nsTime then -- Cheaper and faster than to convert things around.
|
||||
return IsDayCache
|
||||
end
|
||||
return isInDay( nsTime )
|
||||
end
|
||||
|
||||
---Returns true if the current or given time is doing the night.
|
||||
---@param nTime? TimeNumber
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Time.IsNight(nTime)
|
||||
return not StormFox2.Time.IsDay(nTime)
|
||||
end
|
||||
|
||||
---Returns true if the current or given time is between FromTime to ToTime.
|
||||
-- E.g Dinner = StormFox2.Time.IsBetween(700,740)
|
||||
---@param nFromTime TimeNumber
|
||||
---@param nToTime TimeNumber
|
||||
---@param nCurrentTime? TimeNumber
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Time.IsBetween(nFromTime,nToTime,nCurrentTime)
|
||||
if not nCurrentTime then nCurrentTime = StormFox2.Time.Get() end
|
||||
if nFromTime > nToTime then
|
||||
return nCurrentTime >= nFromTime or nCurrentTime <= nToTime
|
||||
end
|
||||
return nFromTime <= nCurrentTime and nToTime >= nCurrentTime
|
||||
end
|
||||
|
||||
---Returns the time between Time and Time2 in minutes.
|
||||
---@param nTime TimeNumber
|
||||
---@param nTime2 TimeNumber
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.DeltaTime(nTime,nTime2)
|
||||
if nTime2 >= nTime then return nTime2 - nTime end
|
||||
return (1440 - nTime) + nTime2
|
||||
end
|
||||
-- Time stamp
|
||||
|
||||
---Returns the current (or given time) hour-number. E.g at 11:43 will return 11.
|
||||
---@param nTime? TimeNumber
|
||||
---@param b12Hour? boolean
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetHours( nTime, b12Hour )
|
||||
if not nTime then nTime = StormFox2.Time.Get() end
|
||||
if not b12Hour then return floor( nTime / 60 ) end
|
||||
local h = floor( nTime / 60 )
|
||||
if h == 0 then
|
||||
h = 12
|
||||
elseif h > 12 then
|
||||
h = h - 12
|
||||
end
|
||||
return h
|
||||
end
|
||||
|
||||
---Returns the current (or given time) minute-number. E.g at 11:43 will return 43.
|
||||
---@param nTime? TimeNumber
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetMinutes( nTime )
|
||||
if not nTime then nTime = StormFox2.Time.Get() end
|
||||
return floor( nTime % 60 )
|
||||
end
|
||||
|
||||
---Returns the current (or given time) seconds-number. E.g at 11:43:22 will return 22.
|
||||
---@param nTime? TimeNumber
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.GetSeconds( nTime )
|
||||
if not nTime then nTime = StormFox2.Time.Get() end
|
||||
return floor( nTime % 1 ) * 60
|
||||
end
|
||||
|
||||
---Returns the current (or given time) "AM" or "PM" string. E.g 20:00 / 8:00 PM will return "PM".
|
||||
---@param nTime? TimeNumber
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Time.GetAMPM( nTime )
|
||||
if not nTime then nTime = StormFox2.Time.Get() end
|
||||
local h = floor( nTime / 60 )
|
||||
if h < 12 or h == 0 then
|
||||
return "AM"
|
||||
end
|
||||
return "PM"
|
||||
end
|
||||
--[[
|
||||
Allows to pause and resume time
|
||||
]]
|
||||
local lastT
|
||||
-- (Internal) Second argument is nil or a table of the old settings from StormFox2.Time.Pause()
|
||||
|
||||
---Returns true if the time is paused.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Time.IsPaused()
|
||||
local dl = day_length:GetValue()
|
||||
local nl = night_length:GetValue()
|
||||
return dl <= 0 and nl <= 0, lastT
|
||||
end
|
||||
if SERVER then
|
||||
---Pauses the time.
|
||||
---@server
|
||||
function StormFox2.Time.Pause()
|
||||
local dl = day_length:GetValue()
|
||||
local nl = night_length:GetValue()
|
||||
if dl <= 0 and nl <= 0 then return end -- Already paused time
|
||||
lastT = { dl, nl }
|
||||
day_length:SetValue( 0 )
|
||||
night_length:SetValue( 0 )
|
||||
end
|
||||
|
||||
---Resumes the time.
|
||||
---@server
|
||||
function StormFox2.Time.Resume()
|
||||
if not StormFox2.Time.IsPaused() then return end
|
||||
if lastT then
|
||||
day_length:SetValue( lastT[1] )
|
||||
night_length:SetValue( lastT[2] )
|
||||
lastT = nil
|
||||
else
|
||||
day_length:SetValue( 12 )
|
||||
night_length:SetValue( 12 )
|
||||
end
|
||||
end
|
||||
end
|
||||
---Returns the seconds until we reached the given time.
|
||||
---Remember to lisen for the hook: "StormFox2.Time.Changed". In case an admin changes the time / time-settings.
|
||||
---@param nTime TimeNumber
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Time.SecondsUntil( nTime )
|
||||
if StormFox2.Time.IsPaused() then return -1 end
|
||||
local c_cycleTime = GetCycleRaw() -- Seconds past sunrise
|
||||
local t_cycleTime = FinsihToCycle( nTime ) -- Seconds past sunrise to said time
|
||||
return ( t_cycleTime - c_cycleTime ) % ( dayLength + nightLength )
|
||||
end
|
||||
|
||||
-- Default Time Display
|
||||
if CLIENT then
|
||||
-- 12h countries
|
||||
local country = system.GetCountry() or "GB"
|
||||
local h12_countries = {"GB","IE","US","CA","AU","NZ","IN","PK","BD","MY","MT","EG","MX","PH"}
|
||||
--[[United Kingdom, Republic of Ireland, the United States, Canada (sorry Quebec),
|
||||
Australia, New Zealand, India, Pakistan, Bangladesh, Malaysia, Malta, Egypt, Mexico and the former American colony of the Philippines
|
||||
]]
|
||||
local default_12 = table.HasValue(h12_countries, country)
|
||||
StormFox2.Setting.AddCL("12h_display",default_12,"Changes how time is displayed.","Time")
|
||||
StormFox2.Setting.SetType( "12h_display", {
|
||||
[false] = "24h clock",
|
||||
[true] = "12h clock"
|
||||
} )
|
||||
---Returns the current time as a string. Useful for displays.
|
||||
---@param nTime? TimeNumber
|
||||
---@return string
|
||||
---@client
|
||||
function StormFox2.Time.GetDisplay(nTime)
|
||||
local use_12 = StormFox2.Setting.GetCache("12h_display",default_12)
|
||||
return StormFox2.Time.TimeToString(nTime,use_12)
|
||||
end
|
||||
|
||||
-- In case the date changes, call the next-day hook
|
||||
hook.Add("StormFox2.data.change","StormFox2.Date.NextDay", function(sKey, zVar, nDelta)
|
||||
if sKey == "day" then
|
||||
hook.Run("StormFox2.Time.NextDay")
|
||||
end
|
||||
end)
|
||||
else
|
||||
local nextDay = -1
|
||||
local _b = false
|
||||
hook.Add("Think", "StormFox2.Time.NextDayCheck", function()
|
||||
if nextDay <= CurTime() then -- Calculate next day
|
||||
local sec = StormFox2.Time.SecondsUntil( 1440 )
|
||||
if sec == -1 then -- Time is paused, will never be next day
|
||||
nextDay = CurTime() + 500
|
||||
else
|
||||
nextDay = CurTime() + sec
|
||||
if _b then
|
||||
hook.Run("StormFox2.Time.NextDay")
|
||||
end
|
||||
_b = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- The time and or timespeed changed. Recalculate when the day changes
|
||||
hook.Add("StormFox2.Time.Changed", "StormFox2.Time.NextDayCalc", function()
|
||||
nextDay = -1
|
||||
_b = false
|
||||
end)
|
||||
|
||||
-- We use the date-functions to increase the day
|
||||
hook.Add("StormFox2.Time.NextDay", "StormFox2.Data.NextDay", function()
|
||||
local nDay = StormFox2.Date.GetYearDay() + 1
|
||||
StormFox2.Date.SetYearDay( nDay )
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
-- A few hooks
|
||||
do
|
||||
local last = -1
|
||||
local loaded = false
|
||||
local function checkDNTrigger()
|
||||
if not loaded then return end
|
||||
local stamp, mapLight = StormFox2.Sky.GetLastStamp()
|
||||
local dN
|
||||
if stamp >= SF_SKY_CEVIL then
|
||||
dN = 1
|
||||
else
|
||||
dN = 0
|
||||
end
|
||||
if last == dN then return end
|
||||
last = dN
|
||||
if dN == 0 then -- Day
|
||||
hook.Run("StormFox2.Time.OnDay")
|
||||
else -- Night
|
||||
hook.Run("StormFox2.Time.OnNight")
|
||||
end
|
||||
end
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.time.strigger",function()
|
||||
timer.Simple(5, function()
|
||||
loaded = true
|
||||
checkDNTrigger()
|
||||
end)
|
||||
end)
|
||||
-- StormFox2.weather.postchange will be called after something changed. We check the stamp in there.
|
||||
hook.Add("StormFox2.weather.postchange", "StormFox2.time.trigger2", checkDNTrigger)
|
||||
end
|
||||
|
||||
--[[
|
||||
Make sure to save the time on shutdown, or when we switch to s_continue while pause is on.
|
||||
]]
|
||||
if SERVER then
|
||||
local function onSave()
|
||||
cookie.Set("sf2_lasttime", tostring( StormFox2.Time.Get( true ) ) )
|
||||
StormFox2.Msg("Saved time.")
|
||||
end
|
||||
if s_continue:GetValue() then
|
||||
hook.Add("ShutDown", "StormFox2.TimeSave",onSave)
|
||||
end
|
||||
s_continue:AddCallback(function(var)
|
||||
if var then
|
||||
hook.Add("ShutDown", "StormFox2.TimeSave",onSave)
|
||||
if curType == SF_PAUSE then
|
||||
cookie.Set("sf2_lasttime", tostring( StormFox2.Time.Get( true ) ) )
|
||||
end
|
||||
else
|
||||
hook.Remove("ShutDown", "StormFox2.TimeSave",onSave)
|
||||
end
|
||||
end, "sf2_savetime")
|
||||
end
|
||||
447
lua/stormfox2/framework/sh_weather_handle.lua
Normal file
447
lua/stormfox2/framework/sh_weather_handle.lua
Normal file
@@ -0,0 +1,447 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Updates the weather for given players
|
||||
|
||||
local lastSet = 0
|
||||
local CurrentWeather
|
||||
local CurrentPercent = 1
|
||||
|
||||
local function isColor(t)
|
||||
return t.r and t.g and t.b and true or false
|
||||
end
|
||||
local function Blender(nFraction, vFrom, vTo) -- Will it blend?
|
||||
-- Nils should be false, if one of them is a boolean
|
||||
if type(vFrom) == "nil" and type(vTo) == "boolean" then
|
||||
vFrom = false
|
||||
end
|
||||
if type(vTo) == "nil" and type(vFrom) == "boolean" then
|
||||
vTo = false
|
||||
end
|
||||
-- If the same value, then return it
|
||||
if vTo == vFrom then return vTo end
|
||||
-- In case of two diffrent variables.
|
||||
if type(vFrom) ~= type(vTo) then
|
||||
StormFox2.Warning("Mixer called with values of two different types[" .. type(vFrom) .. "," .. type(vTo) .. "]")
|
||||
debug.Trace()
|
||||
return vFrom
|
||||
elseif type(vTo) == "string" or type(vTo) == "IMaterial" or type(vTo) == "boolean" then -- String, material or bool. Return vTo.
|
||||
return vTo
|
||||
elseif type(vTo) == "number" then -- Number
|
||||
return Lerp(nFraction, vFrom, vTo)
|
||||
elseif type(vTo) == "table" and isColor(vTo) then -- Color
|
||||
local r = Lerp( nFraction, vFrom.r or 255, vTo.r )
|
||||
local g = Lerp( nFraction, vFrom.g or 255, vTo.g )
|
||||
local b = Lerp( nFraction, vFrom.b or 255, vTo.b )
|
||||
local a = Lerp( nFraction, vFrom.a or 255, vTo.a )
|
||||
return Color( r, g, b, a )
|
||||
end
|
||||
--StormFox2.Warning("ERROR: Unsupported mix value type[" .. type(vTo) .. "]. Returning original value")
|
||||
--debug.Trace()
|
||||
return vFrom
|
||||
end
|
||||
|
||||
local function IsSame(sName, nPercentage, nDelta)
|
||||
if CurrentPercent ~= nPercentage then return false end
|
||||
if not CurrentWeather then return false end
|
||||
return CurrentWeather.Name == sName
|
||||
end
|
||||
|
||||
local function ApplyWeather(sName, nPercentage, nDelta)
|
||||
hook.Run("StormFox2.weather.prechange", sName ,nPercentage )
|
||||
if nDelta and nDelta <= 0 then
|
||||
nDelta = nil
|
||||
elseif nDelta then
|
||||
local sp = StormFox2.Time.GetSpeed_RAW()
|
||||
if sp > 0 then
|
||||
nDelta = nDelta / sp
|
||||
end
|
||||
end
|
||||
local bSameWeather = sName == (CurrentWeather and CurrentWeather.Name or "Clear")
|
||||
if CurrentWeather and CurrentWeather.OnChange then
|
||||
CurrentWeather:OnChange( sName, nPercentage, nDelta )
|
||||
end
|
||||
if CurrentWeather and CurrentWeather.OnRemove and not bSameWeather then
|
||||
CurrentWeather:OnRemove( sName, nPercentage, nDelta )
|
||||
end
|
||||
local clear = StormFox2.Weather.Get( "Clear" )
|
||||
CurrentWeather = StormFox2.Weather.Get( sName )
|
||||
CurrentPercent = nPercentage
|
||||
local stamp = StormFox2.Sky.GetLastStamp()
|
||||
|
||||
if sName == "Clear" then
|
||||
nPercentage = 1
|
||||
end
|
||||
if nPercentage >= 1 then
|
||||
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
|
||||
local v = CurrentWeather:Get( key, stamp )
|
||||
if type(v) == "table" and not (v.r and v.g and v.b) then
|
||||
StormFox2.Data.Set(key, v)
|
||||
else
|
||||
StormFox2.Data.Set(key, v, nDelta)
|
||||
end
|
||||
end
|
||||
elseif nPercentage <= 0 then
|
||||
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
|
||||
StormFox2.Data.Set(key, clear:Get( key, stamp ), nDelta)
|
||||
end
|
||||
else -- Mixing bin
|
||||
for _,key in ipairs( StormFox2.Weather.GetKeys() ) do
|
||||
local var2,b_nomix = CurrentWeather:Get( key, stamp )
|
||||
local d = nDelta
|
||||
if type(var2) == "table" and not (var2.r and var2.g and var2.b) then
|
||||
d = nil
|
||||
end
|
||||
if b_nomix then
|
||||
StormFox2.Data.Set(key, var2, d)
|
||||
else
|
||||
local var1 = clear:Get( key, stamp )
|
||||
if var2 and not var1 then -- This is not a default variable
|
||||
if type(var2) == "number" then
|
||||
var1 = 0
|
||||
end
|
||||
end
|
||||
if not var1 and var2 then -- THis is not a default varable
|
||||
StormFox2.Data.Set(key, var2, d)
|
||||
elseif var1 and var2 then
|
||||
StormFox2.Data.Set(key, Blender(nPercentage, var1, var2), d)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if CurrentWeather.Init and not bSameWeather then
|
||||
CurrentWeather.Init()
|
||||
end
|
||||
if CurrentWeather.Tick10 then
|
||||
CurrentWeather.Tick10()
|
||||
end
|
||||
hook.Run("StormFox2.weather.postchange", sName ,nPercentage, nDelta )
|
||||
end
|
||||
|
||||
hook.Add("StormFox2.Sky.StampChange","StormFox2.Weather.Stamp",function(_,nLerpTime)
|
||||
ApplyWeather(CurrentWeather and CurrentWeather.Name or "Clear", CurrentPercent, nLerpTime)
|
||||
end)
|
||||
|
||||
---Returns the current weather-type.
|
||||
---@return Weather
|
||||
function StormFox2.Weather.GetCurrent()
|
||||
return CurrentWeather or StormFox2.Weather.Get( "Clear" )
|
||||
end
|
||||
|
||||
---Returns the current weather percent.
|
||||
---@return number Percent
|
||||
function StormFox2.Weather.GetPercent()
|
||||
return StormFox2.Data.Get("w_Percentage",CurrentPercent)
|
||||
end
|
||||
|
||||
---Returns the weather percent we're lerping to.
|
||||
---@return number
|
||||
function StormFox2.Weather.GetFinishPercent()
|
||||
return CurrentPercent
|
||||
end
|
||||
|
||||
---Returns the current weather description. Like 'Snow', 'Storm' .. ect.
|
||||
---Second argument isn't translated.
|
||||
---@return string Description
|
||||
---@return string Description_Untranslated
|
||||
function StormFox2.Weather.GetDescription()
|
||||
local c = StormFox2.Weather.GetCurrent()
|
||||
if not c.GetName then
|
||||
return c.Name
|
||||
end
|
||||
local a,b = c:GetName(StormFox2.Time.Get(), StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), StormFox2.Weather.GetPercent())
|
||||
return a,b or a
|
||||
end
|
||||
|
||||
local errM = Material("error")
|
||||
|
||||
---Returns the current weather-icon.
|
||||
---@return userdata Material
|
||||
function StormFox2.Weather.GetIcon()
|
||||
local c = StormFox2.Weather.GetCurrent()
|
||||
if not c.GetIcon then
|
||||
return errM
|
||||
end
|
||||
return c.GetIcon(StormFox2.Time.Get(), StormFox2.Temperature.Get(), StormFox2.Wind.GetForce(), StormFox2.Thunder.IsThundering(), StormFox2.Weather.GetPercent())
|
||||
end
|
||||
|
||||
local SF_UPDATE_WEATHER = 0
|
||||
local SF_INIT_WEATHER = 1
|
||||
|
||||
if SERVER then
|
||||
local l_data
|
||||
|
||||
---Sets the weather.
|
||||
---@server
|
||||
---@param sName string
|
||||
---@param nPercentage number
|
||||
---@param nDelta? number
|
||||
---@return boolean success
|
||||
function StormFox2.Weather.Set( sName, nPercentage, nDelta )
|
||||
if not StormFox2.Setting.GetCache("enable", true) then return end -- Just in case
|
||||
if nDelta and l_data and nDelta == l_data then
|
||||
if IsSame(sName, nPercentage) then return false end
|
||||
end
|
||||
l_data = nDelta
|
||||
-- Default vals
|
||||
if not nDelta then
|
||||
nDelta = 4
|
||||
end
|
||||
if not nPercentage then
|
||||
nPercentage = 1
|
||||
end
|
||||
-- Unknown weathers gets replaced with 'Clear'
|
||||
if not StormFox2.Weather.Get( sName ) then
|
||||
StormFox2.Warning("Unknown weather: " .. tostring(sName))
|
||||
sName = "Clear"
|
||||
end
|
||||
-- In case we set the weather to clear, change it so it is the current weather at 0 instead
|
||||
if sName == "Clear" and CurrentWeather and nDelta > 0 then
|
||||
nPercentage = 0
|
||||
sName = CurrentWeather.Name
|
||||
elseif sName == "Clear" then
|
||||
nPercentage = 1
|
||||
end
|
||||
lastSet = CurTime()
|
||||
net.Start( StormFox2.Net.Weather )
|
||||
net.WriteBit(SF_UPDATE_WEATHER)
|
||||
net.WriteUInt( math.max(0, StormFox2.Data.GetLerpEnd( "w_Percentage" )), 32)
|
||||
net.WriteFloat(nPercentage)
|
||||
net.WriteString(sName)
|
||||
net.WriteFloat(CurTime() + nDelta)
|
||||
net.Broadcast()
|
||||
ApplyWeather(sName, nPercentage, nDelta)
|
||||
if sName == "Clear" then
|
||||
nPercentage = 0
|
||||
end
|
||||
StormFox2.Data.Set("w_Percentage",nPercentage,nDelta)
|
||||
return true
|
||||
end
|
||||
net.Receive( StormFox2.Net.Weather, function(len, ply) -- OI, what weather?
|
||||
local lerpEnd = StormFox2.Data.GetLerpEnd( "w_Percentage" )
|
||||
net.Start( StormFox2.Net.Weather )
|
||||
net.WriteBit(SF_INIT_WEATHER)
|
||||
net.WriteUInt( math.max(0, StormFox2.Data.GetLerpEnd( "w_Percentage" )), 32)
|
||||
net.WriteFloat( CurrentPercent )
|
||||
net.WriteString( StormFox2.Weather.GetCurrent().Name )
|
||||
net.WriteFloat( StormFox2.Data.Get("w_Percentage",CurrentPercent) )
|
||||
net.Send(ply)
|
||||
end)
|
||||
-- Handles the terrain logic
|
||||
timer.Create("StormFox2.terrain.updater", 4, 0, function()
|
||||
local cW = StormFox2.Weather.GetCurrent()
|
||||
local cT = StormFox2.Terrain.GetCurrent()
|
||||
|
||||
if not cW then return end -- No weather!?
|
||||
local terrain = cW:Get("Terrain")
|
||||
if not cT and not terrain then return end -- No terrain detected
|
||||
if cT and terrain and cT == terrain then return end -- Same terrain detected
|
||||
if terrain then -- Switch terraintype out. This can't be the same as the other
|
||||
StormFox2.Terrain.Set(terrain.Name)
|
||||
elseif not terrain and not cT.lock then -- This terrain doesn't have a lock. Reset terrain
|
||||
StormFox2.Terrain.Reset()
|
||||
elseif not terrain and cT.lock then -- Check the lock of cT and see if we can reset
|
||||
if cT:lock() then -- Lock tells us we can reset the terrain
|
||||
StormFox2.Terrain.Reset()
|
||||
end
|
||||
end
|
||||
end)
|
||||
local tS = CurTime()
|
||||
-- In case no weather was set
|
||||
timer.Simple(8, function()
|
||||
-- Clear up weather when it reaches 0
|
||||
timer.Create("StormFox2.weather.clear",1,0,function()
|
||||
if not CurrentWeather then return end
|
||||
if CurrentWeather.Name == "Clear" then return end
|
||||
local p = StormFox2.Weather.GetPercent()
|
||||
if p <= 0 then
|
||||
StormFox2.Weather.Set("Clear", 1, 0)
|
||||
end
|
||||
end)
|
||||
if CurrentWeather then return end
|
||||
StormFox2.Weather.Set("Clear", 1, 0)
|
||||
end)
|
||||
else
|
||||
local hasLocalWeather = false
|
||||
local svWeather
|
||||
local function SetW( sName, nPercentage, nDelta )
|
||||
-- Block same weather
|
||||
if IsSame(sName, nPercentage) then return false end
|
||||
ApplyWeather(sName, nPercentage, nDelta)
|
||||
if sName == "Clear" then
|
||||
nPercentage = 0
|
||||
end
|
||||
StormFox2.Data.Set("w_Percentage",nPercentage,nDelta)
|
||||
end
|
||||
|
||||
---Sets the weather on the client. Server-side stuff won't be set.
|
||||
---@client
|
||||
---@param sName? string
|
||||
---@param nPercentage? number
|
||||
---@param nDelta? number
|
||||
---@param nTemperature? number
|
||||
function StormFox2.Weather.SetLocal( sName, nPercentage, nDelta, nTemperature)
|
||||
-- If nil then remove the local weather
|
||||
if not sName then
|
||||
return StormFox2.Weather.RemoveLocal()
|
||||
end
|
||||
-- Unknown weathers gets replaced with 'Clear'
|
||||
if not StormFox2.Weather.Get( sName ) then
|
||||
StormFox2.Warning("Unknown weather: " .. tostring(sName))
|
||||
sName = "Clear"
|
||||
end
|
||||
if not hasLocalWeather then
|
||||
svWeather = {StormFox2.Weather.GetCurrent().Name, StormFox2.Weather.GetFinishPercent(), StormFox2.Temperature.Get()}
|
||||
end
|
||||
StormFox2.Temperature.SetLocal(nTemperature)
|
||||
-- Block same weather
|
||||
SetW(sName, nPercentage or 1, nDelta)
|
||||
hasLocalWeather = true
|
||||
end
|
||||
|
||||
---Removes the local weather.
|
||||
---@client
|
||||
function StormFox2.Weather.RemoveLocal()
|
||||
if not hasLocalWeather then return end
|
||||
SetW(svWeather[1], svWeather[2], 4)
|
||||
StormFox2.Temperature.SetLocal(nil)
|
||||
svWeather = nil
|
||||
hasLocalWeather = false
|
||||
end
|
||||
net.Receive( StormFox2.Net.Weather, function(len)
|
||||
local flag = net.ReadBit() == SF_UPDATE_WEATHER
|
||||
local wTarget = net.ReadUInt(32)
|
||||
local nPercentage = net.ReadFloat()
|
||||
local sName = net.ReadString()
|
||||
if flag then
|
||||
local nDelta = net.ReadFloat() - CurTime()
|
||||
-- Calculate the time since server set this
|
||||
if not hasLocalWeather then
|
||||
SetW(sName, nPercentage, nDelta)
|
||||
else
|
||||
svWeather[1] = sName
|
||||
svWeather[2] = nPercentage
|
||||
end
|
||||
else
|
||||
local current = net.ReadFloat()
|
||||
if not hasLocalWeather then
|
||||
local secondsLeft = wTarget - CurTime()
|
||||
if secondsLeft <= 0 then
|
||||
SetW(sName, nPercentage, 0)
|
||||
else
|
||||
SetW(sName, current, 0)
|
||||
SetW(sName, nPercentage, secondsLeft)
|
||||
end
|
||||
else
|
||||
svWeather[1] = sName
|
||||
svWeather[2] = nPercentage
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- Ask the server what weather we have
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.terrain", function()
|
||||
net.Start( StormFox2.Net.Weather )
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("Think", "StormFox2.Weather.Think", function()
|
||||
if not CurrentWeather then return end
|
||||
if not CurrentWeather.Think then return end
|
||||
if not StormFox2.Setting.SFEnabled() then return end
|
||||
CurrentWeather:Think()
|
||||
end)
|
||||
|
||||
timer.Create("StormFox2.Weather.tickslow", 1, 0, function()
|
||||
if not CurrentWeather then return end
|
||||
if not CurrentWeather.TickSlow then return end
|
||||
CurrentWeather.TickSlow()
|
||||
end)
|
||||
|
||||
hook.Add("StormFox2.weather.postchange", "StormFox2.weather.slowtickinit", function()
|
||||
if not CurrentWeather then return end
|
||||
if not CurrentWeather.TickSlow then return end
|
||||
CurrentWeather.TickSlow()
|
||||
end)
|
||||
|
||||
if CLIENT then
|
||||
local c_tab = {"PostDrawTranslucentRenderables", "PreDrawTranslucentRenderables", "HUDPaint"}
|
||||
for i,v in ipairs(c_tab) do
|
||||
hook.Add(v, "StormFox2.Weather." .. v, function(...)
|
||||
if not CurrentWeather then return end
|
||||
if not CurrentWeather[v] then return end
|
||||
CurrentWeather[v](...)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Some functions to make it easier.
|
||||
|
||||
---Returns true if it is raining, or if current weather is child of rain.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Weather.IsRaining()
|
||||
local wT = StormFox2.Weather.GetCurrent()
|
||||
if wT.Inherit == "Rain" then return true end
|
||||
if wT.Name ~= "Rain" then return false end
|
||||
return StormFox2.Temperature.Get() > -2 or false
|
||||
end
|
||||
|
||||
---Returns true if it is snowing.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Weather.IsSnowing()
|
||||
local wT = StormFox2.Weather.GetCurrent()
|
||||
if wT.Name ~= "Rain" then return false end
|
||||
return StormFox2.Temperature.Get() <= -2 or false
|
||||
end
|
||||
|
||||
---Returns the rain / snow amount. Between 0 - 1.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Weather.GetRainAmount()
|
||||
if not StormFox2.Weather.IsRaining() then return 0 end
|
||||
return StormFox2.Weather.GetPercent()
|
||||
end
|
||||
|
||||
---Returns true if the current weather is raining, snowing or inherit from rain.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Weather.HasDownfall()
|
||||
local wT = StormFox2.Weather.GetCurrent()
|
||||
if wT.Inherit == "Rain" then return true end
|
||||
return wT.Name == "Rain"
|
||||
end
|
||||
|
||||
-- Downfall
|
||||
|
||||
---Returns true if the entity is hit by rain or any downfall.
|
||||
---@param eEnt Entity
|
||||
---@param bDont_cache boolean
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.DownFall.IsEntityHit(eEnt, bDont_cache)
|
||||
if not StormFox2.Weather.HasDownfall() then return false end
|
||||
return (StormFox2.Wind.IsEntityInWind(eEnt,bDont_cache))
|
||||
end
|
||||
|
||||
---Checks to see if the given point is hit by rain.
|
||||
---@param vPos Vector
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.DownFall.IsPointHit(vPos)
|
||||
if not StormFox2.Weather.HasDownfall() then return false end
|
||||
local t = util.TraceLine( {
|
||||
start = vPos,
|
||||
endpos = vPos + -StormFox2.Wind.GetNorm() * 262144,
|
||||
mask = StormFox2.DownFall.Mask
|
||||
} )
|
||||
return t.HitSky
|
||||
end
|
||||
87
lua/stormfox2/framework/sv_2dskybox.lua
Normal file
87
lua/stormfox2/framework/sv_2dskybox.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
--[[
|
||||
| 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("darken_2dskybox", false, nil, "Effect")
|
||||
|
||||
local convar = GetConVar("sv_skyname")
|
||||
local mat_2dBox = "skybox/" .. convar:GetString()
|
||||
local last_f = 1
|
||||
local function OnChange( str )
|
||||
StormFox2.Map.Set2DSkyBoxDarkness( last_f )
|
||||
end
|
||||
|
||||
cvars.RemoveChangeCallback("sv_skyname", "sf_skynamehook")
|
||||
cvars.AddChangeCallback( "sv_skyname", OnChange, "sf_skynamehook" )
|
||||
|
||||
local t = {"bk", "dn", "ft", "lf", "rt", "up"}
|
||||
|
||||
---Sets the 2D skybox darkness. Mostly used for internal stuff.
|
||||
---@param f number
|
||||
---@param bRemember boolean
|
||||
---@param bDark boolean
|
||||
function StormFox2.Map.Set2DSkyBoxDarkness( f, bRemember, bDark )
|
||||
if bRemember then
|
||||
last_f = f
|
||||
end
|
||||
local sky = convar:GetString()
|
||||
if sky == "painted" then return end
|
||||
if bDark == nil then
|
||||
bDark = StormFox2.Setting.GetCache("darken_2dskybox", false)
|
||||
end
|
||||
if not StormFox2.Setting.GetCache("enable_skybox", true) or not StormFox2.Setting.SFEnabled() or not bDark then
|
||||
f = 1
|
||||
end
|
||||
mat_2dBox = "skybox/" .. sky
|
||||
local vec = Vector( f, f, f)
|
||||
|
||||
for k,v in ipairs( t ) do
|
||||
local m = Material(mat_2dBox .. v)
|
||||
if m:IsError() then continue end
|
||||
m:SetVector("$color", vec)
|
||||
m:SetInt("$nofog", 1)
|
||||
m:SetInt("$ignorez", 1)
|
||||
end
|
||||
end
|
||||
|
||||
StormFox2.Setting.Callback("darken_2dskybox", function(vVar)
|
||||
StormFox2.Map.Set2DSkyBoxDarkness( last_f, false, vVar )
|
||||
end, "darken_2dskybox")
|
||||
|
||||
local function SkyThink(b, str)
|
||||
if not StormFox2.Setting.GetCache("enable_skybox", true) or not StormFox2.Setting.SFEnabled() then return end
|
||||
if b == nil then
|
||||
b = StormFox2.Setting.GetCache("use_2dskybox", false)
|
||||
end
|
||||
if not b then
|
||||
return RunConsoleCommand("sv_skyname", "painted")
|
||||
end
|
||||
local s = str or StormFox2.Setting.GetCache("overwrite_2dskybox", "")
|
||||
if s == "" then
|
||||
local lS = 0
|
||||
if StormFox2.Sky and StormFox2.Sky.GetLastStamp then -- Something happen
|
||||
lS = StormFox2.Sky.GetLastStamp()
|
||||
end
|
||||
local sky_options = StormFox2.Weather.GetCurrent():Get("skyBox", lS)
|
||||
s = (table.Random(sky_options))
|
||||
else
|
||||
StormFox2.Map.Set2DSkyBoxDarkness( last_f )
|
||||
end
|
||||
RunConsoleCommand("sv_skyname", s)
|
||||
end
|
||||
|
||||
StormFox2.Setting.Callback("use_2dskybox",SkyThink,"2dskybox_enable")
|
||||
StormFox2.Setting.Callback("overwrite_2dskybox",function(str) SkyThink(nil, str) end,"2dskybox_enable2")
|
||||
|
||||
hook.Add("StormFox2.weather.postchange", "StormFox2.weather.set2dsky", function( _ )
|
||||
if not StormFox2.Setting.GetCache("use_2dskybox", false) then return end
|
||||
SkyThink()
|
||||
end)
|
||||
339
lua/stormfox2/framework/sv_envioment.lua
Normal file
339
lua/stormfox2/framework/sv_envioment.lua
Normal file
@@ -0,0 +1,339 @@
|
||||
--[[
|
||||
| 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 INVALID_VERTS = 0
|
||||
local WATER_VERTS = 1
|
||||
|
||||
StormFox2.Setting.AddSV("enable_ice",not game.IsDedicated())
|
||||
StormFox2.Setting.AddSV("enable_wateroverlay",true, nil, "Effects")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Localize
|
||||
---------------------------------------------------------------------------]]
|
||||
local round = math.Round
|
||||
local util_TraceLine = util.TraceLine
|
||||
local table_sort = table.sort
|
||||
local LocalPlayer = LocalPlayer
|
||||
--[[-------------------------------------------------------------------------
|
||||
Make a few SurfaceInfo functions.
|
||||
---------------------------------------------------------------------------]]
|
||||
local meta = FindMetaTable("SurfaceInfo")
|
||||
-- Support caching stuff
|
||||
local surf_caching = {}
|
||||
meta.__index = function(a,b)
|
||||
return meta[b] or surf_caching[a] and surf_caching[a][b]
|
||||
end
|
||||
meta.__newindex = function(a,b,c)
|
||||
if not surf_caching[a] then surf_caching[a] = {} end
|
||||
surf_caching[a][b] = c
|
||||
end
|
||||
hook.Add("EntityRemoved", "ClearSurfaceInfo", function(ent)
|
||||
for _,surf in ipairs(ent:GetBrushSurfaces() or {}) do
|
||||
surf_caching[surf] = nil
|
||||
end
|
||||
end)
|
||||
function meta:IsValid( )
|
||||
if self.b_invalid ~= nil then return self.b_invalid end
|
||||
local b = #(self:GetVertices()) > 0
|
||||
self.b_invalid = b
|
||||
return self.b_invalid
|
||||
end
|
||||
function meta:GetVerticesNoParallel( )
|
||||
if not self:IsValid() then return {} end
|
||||
if self.v_vertNP then return table.Copy(self.v_vertNP) end
|
||||
self.v_vertNP = {}
|
||||
local verts = self:GetVertices()
|
||||
for i,cv in ipairs(verts) do
|
||||
local pv,nv = verts[i - 1] or verts[#verts], verts[i + 1] or verts[1]
|
||||
local cP = ( cv - pv ):Cross( nv - pv )
|
||||
if cP.x == 0 and cP.y == 0 and cP.z == 0 then continue end -- parallel vector.
|
||||
table.insert(self.v_vertNP, cv)
|
||||
end
|
||||
return table.Copy(self.v_vertNP)
|
||||
end
|
||||
function meta:GetCenter( )
|
||||
if not self:IsValid() then return end
|
||||
if self.v_cent then return self.v_cent end
|
||||
local verts = self:GetVertices()
|
||||
if #verts < 2 then
|
||||
self.v_cent = verts[1]
|
||||
return self.v_cent
|
||||
end
|
||||
local vmax,vmin = verts[1],verts[1]
|
||||
for i = 2,#verts do
|
||||
vmax[1] = math.max(vmax[1],verts[i][1])
|
||||
vmax[2] = math.max(vmax[2],verts[i][2])
|
||||
vmax[3] = math.max(vmax[3],verts[i][3])
|
||||
vmin[1] = math.min(vmin[1],verts[i][1])
|
||||
vmin[2] = math.min(vmin[2],verts[i][2])
|
||||
vmin[3] = math.min(vmin[3],verts[i][3])
|
||||
end
|
||||
self.v_cent = vmin + (vmax - vmin) / 2
|
||||
return self.v_cent
|
||||
end
|
||||
function meta:GetNormal( )
|
||||
if not self:IsValid() then return end
|
||||
if self.v_norm then return self.v_norm end
|
||||
local p = self:GetVertices()
|
||||
if #p < 3 then return end -- Invalid brush. (Yes this happens)
|
||||
local c = p[1]
|
||||
local s = Vector(0,0,0)
|
||||
for i = 2,#p do
|
||||
s = s + ( p[i] - c ):Cross( (p[i + 1] or p[1]) - c )
|
||||
if s.x ~= 0 and s.y ~= 0 and s.z ~= 0 then -- Check if this isn't a parallel vector.
|
||||
break -- Got a valid norm
|
||||
end
|
||||
end
|
||||
self.v_norm = s:GetNormalized()
|
||||
return self.v_norm
|
||||
end
|
||||
function meta:GetAngles( )
|
||||
if not self:IsValid() then return end
|
||||
if self.a_ang then return self.a_ang end
|
||||
self.a_ang = self:GetNormal():Angle()
|
||||
return self.a_ang
|
||||
end
|
||||
function meta:GetPerimeter( )
|
||||
if not self:IsValid() then return end
|
||||
if self.n_peri then return self.n_peri end
|
||||
local p = self:GetVertices()
|
||||
local n = 0
|
||||
for i = 1,#p do
|
||||
n = n + p[i]:Distance(p[i + 1] or p[1])
|
||||
end
|
||||
self.n_peri = n
|
||||
return self.n_peri
|
||||
end
|
||||
function meta:GetArea( )
|
||||
if not self:IsValid() then return end
|
||||
if self.n_area then return self.n_area end
|
||||
local p = self:GetVertices()
|
||||
local n = #p
|
||||
if n < 3 then -- Invalid shape
|
||||
self.n_area = 0
|
||||
return 0
|
||||
--elseif n == 3 then -- Triangle, but cost more?
|
||||
-- local a,b,c = p[1]:Distance(p[2]),p[2]:Distance(p[3]),p[3]:Distance(p[1])
|
||||
-- local s = (a + b + c) / 2
|
||||
-- t_t[self] = sqrt( s * (s - a) * (s - b) * (s - c) )
|
||||
-- return t_t[self]
|
||||
else -- Any shape
|
||||
local a = Vector(0,0,0)
|
||||
for i,pc in ipairs(p) do
|
||||
local pn = p[i + 1] or p[1]
|
||||
a = a + pc:Cross(pn)
|
||||
end
|
||||
a = a / 2
|
||||
self.n_area = a:Distance(Vector(0,0,0))
|
||||
return self.n_area
|
||||
end
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Make some adv SurfaceInfo functions.
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:GetUVVerts() -- Creates UV-data out from the shape.
|
||||
if not self:IsValid() then return end
|
||||
if self.t_uv then return table.Copy(self.t_uv) end
|
||||
local t = self:GetVerticesNoParallel()
|
||||
local a = self:GetNormal():Angle()
|
||||
local c = self:GetCenter()
|
||||
local vmin,vmax
|
||||
for i,v in ipairs(t) do
|
||||
t[i] = (t[i] - c)
|
||||
t[i]:Rotate(a)
|
||||
if not vmin then
|
||||
vmin = Vector(t[i].x,t[i].y,t[i].z)
|
||||
vmax = Vector(t[i].x,t[i].y,t[i].z)
|
||||
else
|
||||
for ii = 1,3 do
|
||||
vmin[ii] = math.min(vmin[ii],t[i][ii])
|
||||
vmax[ii] = math.max(vmax[ii],t[i][ii])
|
||||
end
|
||||
end
|
||||
end
|
||||
local y_r = vmax.z - vmin.z
|
||||
local x_r,x_r2 = vmax.x - vmin.x,vmax.y - vmin.y
|
||||
local min_x = vmin.x
|
||||
local i2 = 1
|
||||
if x_r2 > x_r then
|
||||
x_r = x_r2
|
||||
i2 = 2
|
||||
min_x = vmin.y
|
||||
end
|
||||
local new_t = {}
|
||||
for i = 1,#t do
|
||||
table.insert(new_t, {u = (t[i][i2] - min_x) / x_r,v = (t[i].z - vmin.z) / y_r})
|
||||
end
|
||||
self.t_uv = new_t
|
||||
return table.Copy(self.t_uv)
|
||||
end
|
||||
function meta:GetMesh() -- Generates a mesh-table for the surfaceinfo.
|
||||
if not self:IsValid() then return end
|
||||
if self.t_mesh then return table.Copy(self.t_mesh) end
|
||||
local verts = self:GetVerticesNoParallel()
|
||||
local n = self:GetNormal()
|
||||
-- Calc the height and width
|
||||
local h_max,h_min = verts[1].z,verts[1].z
|
||||
for i = 2,#verts do
|
||||
local h = verts[i].z
|
||||
h_max = math.max(h_max,h)
|
||||
h_min = math.min(h_min,h)
|
||||
end
|
||||
local uvt = self:GetUVVerts()
|
||||
local t = {}
|
||||
for i = 1,3 do
|
||||
table.insert(t, {pos = verts[i], u = uvt[i].u,v = uvt[i].v, normal = n})
|
||||
end
|
||||
for i = 4,#verts do
|
||||
table.insert(t, {pos = verts[1], u = uvt[1].u,v = uvt[1].v, normal = n})
|
||||
table.insert(t, {pos = verts[i - 1], u = uvt[i - 1].u,v = uvt[i - 1].v, normal = n})
|
||||
table.insert(t, {pos = verts[i], u = uvt[i].u,v = uvt[i].v, normal = n})
|
||||
end
|
||||
self.t_mesh = t
|
||||
return table.Copy(self.t_mesh)
|
||||
end
|
||||
function meta:GetMinSide()
|
||||
if not self:IsValid() then return end
|
||||
if self.n_midi then return self.n_midi end
|
||||
local mi,ma
|
||||
local p = self:GetVertices()
|
||||
for i = 1,#p do
|
||||
if not mi then
|
||||
mi = p[i]:Distance(p[i + 1] or p[1])
|
||||
ma = mi
|
||||
else
|
||||
mi = math.min(mi,p[i]:Distance(p[i + 1] or p[1]))
|
||||
ma = math.max(ma,p[i]:Distance(p[i + 1] or p[1]))
|
||||
end
|
||||
end
|
||||
self.n_midi = mi
|
||||
self.n_madi = ma
|
||||
return mi
|
||||
end
|
||||
function meta:GetMaxSide()
|
||||
if not self:IsValid() then return end
|
||||
if self.n_madi then return self.n_madi end
|
||||
self:GetMinSide()
|
||||
return self.n_madi
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Generate meshes and env-points out from the map-data.
|
||||
---------------------------------------------------------------------------]]
|
||||
-- New surface functions
|
||||
local function SurfaceInfo_GetType( eEnt, SurfaceInfo )
|
||||
if #SurfaceInfo:GetVertices() < 3 then return INVALID_VERTS end
|
||||
if SurfaceInfo:IsWater() then -- Water
|
||||
return WATER_VERTS
|
||||
end
|
||||
return INVALID_VERTS
|
||||
end
|
||||
|
||||
|
||||
local ice = Material("stormfox2/effects/ice_water")
|
||||
local ice_size = 500
|
||||
local vec_ex = Vector(0,0,1)
|
||||
|
||||
STORMFOX_WATERMESHCOLLISON = {}
|
||||
|
||||
local scan = function() -- Locates all surfaceinfos we need.
|
||||
StormFox2.Msg("Scanning surfaces ..")
|
||||
surfaceinfos = {}
|
||||
-- Scan all brushsurfaces and grab the glass/windows, water and metal. Put them in a table with matching normal.
|
||||
for i,v in ipairs( game.GetWorld():GetBrushSurfaces() ) do
|
||||
if not v then continue end
|
||||
if not v:IsValid() then continue end
|
||||
if not v:IsWater() then continue end
|
||||
local v_type = SurfaceInfo_GetType( game.GetWorld(), v )
|
||||
if v_type == INVALID_VERTS then continue end -- Invalid or doesn't have a type.
|
||||
if not surfaceinfos[v_type] then surfaceinfos[v_type] = {} end
|
||||
table.insert(surfaceinfos[v_type], {v, v:GetCenter()} )
|
||||
end
|
||||
coroutine.yield()
|
||||
-- Generate water mesh
|
||||
if surfaceinfos[WATER_VERTS] then
|
||||
StormFox2.Msg("Generating ice-mesh [" .. #surfaceinfos[WATER_VERTS] .. "] ")
|
||||
local mesh = {}
|
||||
for i,v in ipairs(surfaceinfos[WATER_VERTS]) do
|
||||
if StormFox2.Map.IsInside( v[2] ) then
|
||||
local t = v[1]:GetVertices()
|
||||
if #t >= 3 then
|
||||
local t2 = {}
|
||||
if #t == 3 then
|
||||
for i = 1,3 do
|
||||
table.insert(t2, t[i] + vec_ex)
|
||||
table.insert(t2, t[i] - vec_ex)
|
||||
end
|
||||
table.insert(t2, t[3] + vec_ex)
|
||||
table.insert(t2, t[3] - vec_ex)
|
||||
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
||||
elseif #t == 4 then
|
||||
for i = 1,4 do
|
||||
table.insert(t2, t[i] + vec_ex)
|
||||
table.insert(t2, t[i] - vec_ex)
|
||||
end
|
||||
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
||||
else
|
||||
for i = 1,#t do
|
||||
table.insert(t2, t[i] + vec_ex)
|
||||
table.insert(t2, t[i] - vec_ex)
|
||||
end
|
||||
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
coroutine.yield(true)
|
||||
end
|
||||
|
||||
local cor_scan = coroutine.wrap(scan)
|
||||
local function StartGenerating()
|
||||
timer.Create("SF_ENV_SCAN", 0.2, 0, function()
|
||||
if cor_scan() then
|
||||
cor_scan = nil
|
||||
timer.Remove("SF_ENV_SCAN")
|
||||
StormFox2.Msg("Meshes completed.")
|
||||
end
|
||||
end)
|
||||
hook.Remove("StormFox2.InitPostEntity", "StormFox_ENV_SCAN")
|
||||
end
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox_ENV_SCAN", StartGenerating)
|
||||
|
||||
local bIce = false
|
||||
local function SpawnIce()
|
||||
for k,v in ipairs(ents.FindByClass("stormfox_mapice")) do
|
||||
v:Remove()
|
||||
end
|
||||
local e = ents.Create("stormfox_mapice")
|
||||
e:SetPos(Vector(0,0,0))
|
||||
e:Spawn()
|
||||
bIce = true
|
||||
end
|
||||
|
||||
local function RemoveIce()
|
||||
bIce = false
|
||||
for k,v in ipairs(ents.FindByClass("stormfox_mapice")) do
|
||||
v:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
timer.Create("stormfox2.spawnice", 8, 0, function()
|
||||
if not StormFox2.Setting.GetCache("enable_ice") then
|
||||
if bIce then
|
||||
RemoveIce()
|
||||
end
|
||||
return
|
||||
end
|
||||
if bIce and StormFox2.Temperature.Get() > -1 then
|
||||
RemoveIce()
|
||||
elseif not bIce and StormFox2.Temperature.Get() <= -8 then
|
||||
SpawnIce()
|
||||
end
|
||||
end)
|
||||
114
lua/stormfox2/framework/sv_heavens.lua
Normal file
114
lua/stormfox2/framework/sv_heavens.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
| 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.Sun.SetTimeUp(nTime) Sets how long the sun is on the sky.
|
||||
StormFox2.Sun.IsUp() Returns true if the sun is on the sky.
|
||||
|
||||
|
||||
StormFox2.Moon.SetTimeUp(nTime) Sets how long the moon is on the sky.
|
||||
|
||||
---------------------------------------------------------------------------]]
|
||||
local clamp = math.Clamp
|
||||
|
||||
StormFox2.Sun = StormFox2.Sun or {}
|
||||
StormFox2.Moon = StormFox2.Moon or {}
|
||||
StormFox2.Sky = StormFox2.Sky or {}
|
||||
|
||||
-- SunRise and SunSet
|
||||
|
||||
---Sets the time for sunrise.
|
||||
---@param nTime TimeNumber
|
||||
---@server
|
||||
function StormFox2.Sun.SetSunRise(nTime)
|
||||
StormFox2.Setting.Set("sunrise", nTime)
|
||||
end
|
||||
|
||||
---Sets the tiem for sunsets.
|
||||
---@param nTime TimeNumber
|
||||
---@server
|
||||
function StormFox2.Sun.SetSunSet(nTime)
|
||||
StormFox2.Setting.Set("sunset", nTime)
|
||||
end
|
||||
|
||||
---Sets the sunyaw. This will also affect the moon.
|
||||
---@param nYaw number
|
||||
---@server
|
||||
function StormFox2.Sun.SetYaw(nYaw)
|
||||
StormFox2.Setting.Set("sunyaw",nYaw)
|
||||
end
|
||||
|
||||
---Sets the sunsize. (Default: 30)
|
||||
---@param n number
|
||||
---@server
|
||||
function StormFox2.Sun.SetSize(n)
|
||||
StormFox2.Network.Set("sun_size",n)
|
||||
end
|
||||
|
||||
---Sets the suncolor.
|
||||
---@param cColor table
|
||||
---@deprecated
|
||||
---@server
|
||||
function StormFox2.Sun.SetColor(cColor)
|
||||
StormFox2.Network.Set("sunColor",cColor)
|
||||
end
|
||||
|
||||
-- Moon
|
||||
--[[-------------------------------------------------------------------------
|
||||
Sets the moon phase, and increases it once pr day
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Add("StormFox2.Time.NextDay","StormFox2.MoonPhase",function()
|
||||
StormFox2.Moon.SetPhase( StormFox2.Moon.GetPhase() + 1 )
|
||||
end)
|
||||
|
||||
---Sets the moon phase. A number between 0 and 7.
|
||||
---@param moon_phase number
|
||||
---@server
|
||||
function StormFox2.Moon.SetPhase( moon_phase )
|
||||
StormFox2.Network.Set("moon_phase",moon_phase % 8)
|
||||
end
|
||||
|
||||
StormFox2.Moon.SetPhase( math.random(0, 7) )
|
||||
|
||||
-- Skybox
|
||||
local function SkyTick(b)
|
||||
b = b and StormFox2.Setting.SFEnabled()
|
||||
if b then -- Reenable skybox
|
||||
local _2d = StormFox2.Setting.GetCache("use_2dskybox", false)
|
||||
if not _2d then
|
||||
RunConsoleCommand("sv_skyname", "painted")
|
||||
else
|
||||
local sky_over = StormFox2.Setting.GetCache("overwrite_2dskybox", "")
|
||||
if sky_over == "" then
|
||||
sky_over = StormFox2.Weather.GetCurrent():Get("skyBox",StormFox2.Sky.GetLastStamp()) or "skybox/sky_day02_06_hdrbk"
|
||||
if type(sky_over) == "table" then
|
||||
sky_over = table.Random(sky_over)
|
||||
end
|
||||
end
|
||||
RunConsoleCommand("sv_skyname", sky_over)
|
||||
end
|
||||
else -- Disable skybox
|
||||
local map_ent = StormFox2.Map.Entities()[1]
|
||||
if not map_ent then
|
||||
StormFox2.Warning("No map-entity?")
|
||||
RunConsoleCommand("sv_skyname", "skybox/sky_day02_06_hdrbk")
|
||||
return
|
||||
end
|
||||
local sky_name = map_ent["skyname"] or "skybox/sky_day02_06_hdrbk"
|
||||
StormFox2.Map.Set2DSkyBoxDarkness( 1 )
|
||||
RunConsoleCommand("sv_skyname", sky_name)
|
||||
end
|
||||
end
|
||||
local func = function(b)
|
||||
SkyTick(StormFox2.Setting.GetCache("enable_skybox", true))
|
||||
end
|
||||
StormFox2.Setting.Callback("enable", func, "disable_heavens")
|
||||
StormFox2.Setting.Callback("clenable", func, "disable_heavenscl")
|
||||
StormFox2.Setting.Callback("enable_skybox",SkyTick,"enable_skybox_call")
|
||||
276
lua/stormfox2/functions/cl_clouds.lua
Normal file
276
lua/stormfox2/functions/cl_clouds.lua
Normal 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)
|
||||
129
lua/stormfox2/functions/cl_contextmenu.lua
Normal file
129
lua/stormfox2/functions/cl_contextmenu.lua
Normal 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
|
||||
} )
|
||||
204
lua/stormfox2/functions/cl_effects.lua
Normal file
204
lua/stormfox2/functions/cl_effects.lua
Normal 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)
|
||||
269
lua/stormfox2/functions/cl_localization.lua
Normal file
269
lua/stormfox2/functions/cl_localization.lua
Normal 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
|
||||
334
lua/stormfox2/functions/cl_menu.lua
Normal file
334
lua/stormfox2/functions/cl_menu.lua
Normal 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
|
||||
1220
lua/stormfox2/functions/cl_menu_sv.lua
Normal file
1220
lua/stormfox2/functions/cl_menu_sv.lua
Normal file
File diff suppressed because it is too large
Load Diff
171
lua/stormfox2/functions/cl_sky.lua
Normal file
171
lua/stormfox2/functions/cl_sky.lua
Normal 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)
|
||||
33
lua/stormfox2/functions/cl_spook.lua
Normal file
33
lua/stormfox2/functions/cl_spook.lua
Normal 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)
|
||||
499
lua/stormfox2/functions/cl_weather_gen.lua
Normal file
499
lua/stormfox2/functions/cl_weather_gen.lua
Normal 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
|
||||
189
lua/stormfox2/functions/sh_concommands.lua
Normal file
189
lua/stormfox2/functions/sh_concommands.lua
Normal 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)
|
||||
685
lua/stormfox2/functions/sh_controller.lua
Normal file
685
lua/stormfox2/functions/sh_controller.lua
Normal 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")
|
||||
288
lua/stormfox2/functions/sh_fog.lua
Normal file
288
lua/stormfox2/functions/sh_fog.lua
Normal 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)
|
||||
298
lua/stormfox2/functions/sh_footprints.lua
Normal file
298
lua/stormfox2/functions/sh_footprints.lua
Normal 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)
|
||||
122
lua/stormfox2/functions/sh_tonemapcontroller.lua
Normal file
122
lua/stormfox2/functions/sh_tonemapcontroller.lua
Normal 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)
|
||||
205
lua/stormfox2/functions/sh_tweaks.lua
Normal file
205
lua/stormfox2/functions/sh_tweaks.lua
Normal 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
|
||||
40
lua/stormfox2/functions/sh_vfire.lua
Normal file
40
lua/stormfox2/functions/sh_vfire.lua
Normal 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)
|
||||
223
lua/stormfox2/functions/sv_mapio.lua
Normal file
223
lua/stormfox2/functions/sv_mapio.lua
Normal 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)
|
||||
17
lua/stormfox2/functions/sv_menu.lua
Normal file
17
lua/stormfox2/functions/sv_menu.lua
Normal 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)
|
||||
901
lua/stormfox2/functions/sv_weather_gen.lua
Normal file
901
lua/stormfox2/functions/sv_weather_gen.lua
Normal 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)
|
||||
275
lua/stormfox2/lib/cl_ambiencesnd.lua
Normal file
275
lua/stormfox2/lib/cl_ambiencesnd.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
SF_AMB_SND = SF_AMB_SND or {}
|
||||
SF_AMB_CHANNEL = SF_AMB_CHANNEL or {} -- [snd]{station, target_vol, current_vol}
|
||||
|
||||
StormFox2.Ambience = {}
|
||||
|
||||
--[[
|
||||
- Outside Constant
|
||||
- Near Outside 3D
|
||||
- Near Window By Distance to nearest
|
||||
- Roof By Distance to nearest
|
||||
- Glass Roof (Like window) By Distance to nearest
|
||||
- Metal Roof By Distance to nearest
|
||||
]]
|
||||
|
||||
--[[ Enums
|
||||
SF_AMB_CONSTANT = 0
|
||||
SF_AMB_DISTANCE = 1
|
||||
SF_AMB_FAKE3D = 2 -- Pans the sound towards the point
|
||||
SF_AMB_USE3D = 3
|
||||
]]
|
||||
|
||||
SF_AMB_OUTSIDE = 0 -- CONSTANT VOLUME
|
||||
SF_AMB_NEAR_OUTSIDE = 1 -- DISTANCE VOLUME
|
||||
SF_AMB_WINDOW = 2 -- DISTANCE VOLUME
|
||||
SF_AMB_UNDER_WATER = 3 -- CONSTANT VOLUME
|
||||
SF_AMB_UNDER_WATER_Z = 4 -- Z-DISTANCE VOLUME (The distance to surface)
|
||||
SF_AMB_ROOF_ANY = 5 -- Z-DISTANCE (SF_AMB_ROOF_CONCRETE and SF_AMB_ROOF_GROUND will be ignored)
|
||||
SF_AMB_ROOF_GLASS = 6 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_METAL = 7 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_WOOD = 8 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_CONCRETE = 9 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_GROUND = 10-- Z-DISTANCE (Default roof)
|
||||
SF_AMB_ROOF_WATER = 11-- Z-DISTANCE
|
||||
|
||||
-- Smooth the volume of SF_AMB_CHANNEL
|
||||
hook.Add("Think", "StormFox2.Ambiences.Smooth", function()
|
||||
for snd,t in pairs( SF_AMB_CHANNEL ) do
|
||||
if not IsValid( t[1] ) then -- In case something goes wrong. Delete the channel
|
||||
SF_AMB_CHANNEL[snd] = nil
|
||||
continue
|
||||
end
|
||||
-- Calc the new volume
|
||||
local c_vol = t[3]
|
||||
local newvol = math.Approach( c_vol, t[2], FrameTime() )
|
||||
if c_vol == newvol then continue end
|
||||
if newvol <= 0 then
|
||||
-- Stop the sound and remove channel
|
||||
t[1]:Stop()
|
||||
SF_AMB_CHANNEL[snd] = nil
|
||||
else
|
||||
if system.HasFocus() then -- We don't want sound playing when gmod is unfocused.
|
||||
t[1]:SetVolume( newvol )
|
||||
else
|
||||
t[1]:SetVolume( 0 )
|
||||
end
|
||||
SF_AMB_CHANNEL[snd][3] = newvol
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local AMB_LOAD = {}
|
||||
-- Handles the sound-channel.
|
||||
local function RequestChannel( snd )
|
||||
if AMB_LOAD[snd] then return end -- Already loading, or error
|
||||
if SF_AMB_CHANNEL[snd] then return end -- Already loaded
|
||||
AMB_LOAD[snd] = true
|
||||
sound.PlayFile( snd, "noblock noplay", function( station, errCode, errStr )
|
||||
if ( IsValid( station ) ) then
|
||||
SF_AMB_CHANNEL[snd] = {station, 0.1, 0}
|
||||
station:SetVolume( 0 )
|
||||
station:EnableLooping( true )
|
||||
station:Play()
|
||||
AMB_LOAD[snd] = nil -- Allow it to be loaded again
|
||||
else
|
||||
if errCode == 1 then
|
||||
StormFox2.Warning("Sound Error! [1] Memory error.")
|
||||
elseif errCode == 2 then
|
||||
StormFox2.Warning("Sound Error! [2] Unable to locate or open: " .. snd .. ".")
|
||||
else
|
||||
StormFox2.Warning("Sound Error! [" .. errCode .. "] " .. errStr .. ".")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local snd_meta = {}
|
||||
snd_meta.__index = snd_meta
|
||||
|
||||
---Creates an ambience sound and returns a sound-object.
|
||||
---@param snd string
|
||||
---@param SF_AMB_TYPE number
|
||||
---@param vol_scale? number
|
||||
---@param min? number
|
||||
---@param max? number
|
||||
---@param playrate? number
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.Ambience.CreateAmbienceSnd( snd, SF_AMB_TYPE, vol_scale, min, max, playrate )
|
||||
local t = {}
|
||||
t.snd = "sound/" .. snd
|
||||
t.m_vol = vol_scale or 1
|
||||
t.min = min or 60
|
||||
t.max = max or 300
|
||||
t.SF_AMB_TYPE = SF_AMB_TYPE or SF_AMB_OUTSIDE
|
||||
t.playbackrate = playrate or 1
|
||||
setmetatable( t , snd_meta )
|
||||
return t
|
||||
end
|
||||
|
||||
---Returns the current sound channels / data.
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.Ambience.DebugList()
|
||||
return SF_AMB_CHANNEL
|
||||
end
|
||||
-- Sets the scale of the sound
|
||||
function snd_meta:SetVolume( num )
|
||||
self.m_vol = math.Clamp(num, 0, 2) -- Just in case
|
||||
end
|
||||
-- Doesn't work on sounds with SF_AMB_OUTSIDE or SF_AMB_UNDER_WATER
|
||||
function snd_meta:SetFadeDistance( min, max )
|
||||
self.min = min
|
||||
self.max = max
|
||||
end
|
||||
-- Set playback rate.
|
||||
function snd_meta:SetPlaybackRate( n )
|
||||
self.playbackrate = n or 1
|
||||
end
|
||||
-- Adds ambience for weather
|
||||
hook.Add("stormfox2.preloadweather", "StormFox2.Amb.Create", function( w_meta )
|
||||
function w_meta:AddAmbience( amb_object )
|
||||
if not self.ambience_tab then self.ambience_tab = {} end
|
||||
table.insert(self.ambience_tab, amb_object)
|
||||
end
|
||||
function w_meta:ClearAmbience()
|
||||
self.ambience_tab = {}
|
||||
end
|
||||
hook.Remove("stormfox2.preloadweather", "StormFox2.Amb.Create")
|
||||
end)
|
||||
-- Applies the ambience sound
|
||||
local function check(SF_AMB_TYPE, env)
|
||||
if SF_AMB_TYPE == SF_AMB_NEAR_OUTSIDE and env.nearest_outside then return env.nearest_outside end
|
||||
if SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_window then return env.nearest_window end
|
||||
end
|
||||
|
||||
local p_br = {}
|
||||
-- Forces a sound to play
|
||||
local fP
|
||||
|
||||
---Insers ambience sound and forces it to play.
|
||||
---@param snd string
|
||||
---@param nVolume number
|
||||
---@param playbackSpeed number
|
||||
---@client
|
||||
function StormFox2.Ambience.ForcePlay( snd, nVolume, playbackSpeed )
|
||||
if string.sub(snd, 0, 6) ~= "sound/" then
|
||||
snd = "sound/" .. snd
|
||||
end
|
||||
fP[snd] = nVolume
|
||||
p_br[snd] = playbackSpeed or 1
|
||||
end
|
||||
hook.Add("Think", "StormFox2.Ambiences.Logic", function()
|
||||
if not StormFox2 or not StormFox2.Weather or not StormFox2.Weather.GetCurrent then return end
|
||||
local c = StormFox2.Weather.GetCurrent()
|
||||
local v_pos = StormFox2.util.GetCalcView().pos
|
||||
local env = StormFox2.Environment.Get()
|
||||
-- Set all target volume to 0
|
||||
for _,t2 in pairs( SF_AMB_CHANNEL ) do
|
||||
t2[2] = 0
|
||||
end
|
||||
-- Generate a list of all sounds the client should hear. And set the the volume
|
||||
local t = {}
|
||||
if c.ambience_tab and StormFox2.Setting.SFEnabled() then
|
||||
for _,amb_object in ipairs( c.ambience_tab ) do
|
||||
local c_vol = t[amb_object.snd] or 0
|
||||
-- WATER
|
||||
if env.in_water then -- All sounds gets ignored in water. Exp SF_AMB_INWATER
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_INWATER then
|
||||
if c_vol > amb_object.m_vol then
|
||||
continue
|
||||
end
|
||||
c_vol = amb_object.m_vol
|
||||
elseif env.outside and amb_object.SF_AMB_TYPE == SF_AMB_UNDER_WATER_Z then
|
||||
local dis = env.in_water - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
-- OUTSIDE
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_OUTSIDE and env.outside then
|
||||
if c_vol > amb_object.m_vol then
|
||||
continue
|
||||
end
|
||||
c_vol = amb_object.m_vol -- Outside is a constant volume
|
||||
-- ROOFS
|
||||
elseif amb_object.SF_AMB_TYPE >= SF_AMB_ROOF_ANY and amb_object.SF_AMB_TYPE <= SF_AMB_ROOF_WATER then
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_ANY and env.roof_z then
|
||||
if env.roof_type ~= SF_AMB_ROOF_CONCRETE and env.roof_type ~= SF_AMB_ROOF_GROUND then
|
||||
local dis = env.roof_z - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
elseif env.roof_z and env.roof_type then
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GROUND and env.roof_type == SF_DOWNFALL_HIT_GROUND then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GLASS and env.roof_type == SF_DOWNFALL_HIT_GLASS then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_METAL and env.roof_type == SF_DOWNFALL_HIT_METAL then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WOOD and env.roof_type == SF_DOWNFALL_HIT_WOOD then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_CONCRETE and env.roof_type == SF_DOWNFALL_HIT_CONCRETE then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WATER and env.roof_type == SF_DOWNFALL_HIT_WATER then
|
||||
else
|
||||
continue
|
||||
end
|
||||
local dis = env.roof_z - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
else
|
||||
local pos = check( amb_object.SF_AMB_TYPE, env )
|
||||
if not pos then continue end
|
||||
local dis = pos:Distance( v_pos )
|
||||
--if amb_object.SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_outside then
|
||||
-- dis = math.max(dis, 250 - env.nearest_outside:Distance(v_pos))
|
||||
|
||||
--end
|
||||
if dis > amb_object.max then continue end -- Too far away
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if vol <= 0 then continue end -- Vol too low
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
if c_vol > 0 then
|
||||
t[amb_object.snd] = c_vol
|
||||
p_br[amb_object.snd] = amb_object.playbackrate
|
||||
end
|
||||
end
|
||||
end
|
||||
fP = t
|
||||
hook.Run("StormFox2.Ambiences.OnSound")
|
||||
-- Set the target volume
|
||||
for snd, vol in pairs( t ) do
|
||||
if not SF_AMB_CHANNEL[snd] then -- Request to create the sound channel
|
||||
RequestChannel( snd )
|
||||
else
|
||||
SF_AMB_CHANNEL[snd][2] = vol -- Set the target volume
|
||||
if IsValid( SF_AMB_CHANNEL[snd][1] ) then
|
||||
if SF_AMB_CHANNEL[snd][1]:GetState() == 0 then -- Somehow stopped
|
||||
SF_AMB_CHANNEL[snd][1]:Play()
|
||||
end
|
||||
if SF_AMB_CHANNEL[snd][1]:GetPlaybackRate() ~= p_br[snd] then
|
||||
SF_AMB_CHANNEL[snd][1]:SetPlaybackRate(p_br[snd])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
236
lua/stormfox2/lib/cl_defaultparticles.lua
Normal file
236
lua/stormfox2/lib/cl_defaultparticles.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Rain and show particles are a bit large. So we init them here
|
||||
|
||||
if not StormFox2.Misc then StormFox2.Misc = {} end
|
||||
local m_snow = Material("particle/snow")
|
||||
local m_snow_multi = Material("stormfox2/effects/snow-multi.png")
|
||||
local m_rain = Material("stormfox2/effects/raindrop.png")
|
||||
local m_rain_medium = Material("stormfox2/effects/raindrop2.png")
|
||||
local m_rain_fog = Material("particle/particle_smokegrenade")
|
||||
local rainsplash_w = Material("effects/splashwake3")
|
||||
local rainsplash = Material("stormfox2/effects/rain_splash")
|
||||
local m_noise = Material("particle/particle_noisesphere")
|
||||
local m_fog = Material("particle/smokesprites_0014")
|
||||
|
||||
-- Hit particles
|
||||
local function MakeRing( vPos, vNormal, L )
|
||||
local p = StormFox2.DownFall.AddParticle( rainsplash_w, vPos, true )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
p:SetStartSize(8)
|
||||
p:SetEndSize(40)
|
||||
p:SetDieTime(1)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(255,25 + math.random(7,10) + L * 0.9))
|
||||
end
|
||||
local function MakeSplash( vPos, vNormal, L, Part )
|
||||
local p = StormFox2.DownFall.AddParticle( rainsplash, vPos, false )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
local _,s = Part:GetSize()
|
||||
p:SetStartSize(s / 10)
|
||||
p:SetEndSize(s / 2.5)
|
||||
p:SetDieTime(0.15)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(105, 30 + L * 0.9))
|
||||
end
|
||||
local function MakeSnowflake( vPos, vNormal, L, Part )
|
||||
local p = StormFox2.DownFall.AddParticle( m_snow, vPos - vNormal, false )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
p:SetStartSize(math.min(2,Part:GetSize()))
|
||||
p:SetEndSize(0)
|
||||
p:SetDieTime(5)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(255, 10 + L))
|
||||
end
|
||||
|
||||
local pT = function(self)
|
||||
local n = math.min(15, StormFox2.Weather.GetLuminance() * 0.75)
|
||||
if self:GetLifeTime() < self:GetDieTime() * .25 then
|
||||
self:SetStartAlpha(0)
|
||||
self:SetEndAlpha( n * 8 )
|
||||
elseif self:GetLifeTime() < self:GetDieTime() * .5 then
|
||||
self:SetStartAlpha(n)
|
||||
self:SetEndAlpha( n )
|
||||
else
|
||||
self:SetStartAlpha(n * 2)
|
||||
self:SetEndAlpha( 0 )
|
||||
end
|
||||
self:SetNextThink( CurTime() )
|
||||
end
|
||||
|
||||
local LM = 0
|
||||
local vector_zero = Vector(0,0,0)
|
||||
local function MakeMist( vPos, L, Part)
|
||||
if LM > CurTime() then return end
|
||||
--LM = CurTime() + 0.1
|
||||
local w = StormFox2.Wind.GetVector()
|
||||
local v = Vector(w.x * 8 + math.Rand(-10, 10), w.y * 8 + math.Rand(-10, 10) ,math.Rand(0, 10))
|
||||
local ss = math.Rand(75,180)
|
||||
local es = math.Rand(75,180)
|
||||
local p = StormFox2.DownFall.AddParticle( m_rain_fog, vPos + Vector(0,0,math.max(es,ss) / 2), false )
|
||||
if not p then return end
|
||||
p:SetAirResistance(0)
|
||||
p:SetNextThink( CurTime() )
|
||||
p:SetDieTime( math.random(10, 15))
|
||||
p:SetRoll( math.Rand(0,360) )
|
||||
p:SetStartSize(ss)
|
||||
p:SetEndSize(es)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(0)
|
||||
p:SetThinkFunction(pT)
|
||||
local c = Part:GetColor() or color_white
|
||||
p:SetColor( c.r, c.g, c.b )
|
||||
p:SetVelocity(v)
|
||||
p:SetCollide( true )
|
||||
p:SetCollideCallback( function( part ) --This is an in-line function
|
||||
part:SetVelocity(vector_zero)
|
||||
p:SetLifeTime(25)
|
||||
end )
|
||||
end
|
||||
|
||||
-- Make big cloud particles size shared, to fix size hitting
|
||||
|
||||
local init = function()
|
||||
local fog_template = StormFox2.DownFall.CreateTemplate(m_fog, false)
|
||||
StormFox2.Misc.fog_template = fog_template
|
||||
--fog_template:SetSpeed(0.1)
|
||||
fog_template:SetSize(250, 250)
|
||||
function fog_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
elseif nHitType == SF_DOWNFALL_HIT_GLASS then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
|
||||
local rain_template = StormFox2.DownFall.CreateTemplate(m_rain, true)
|
||||
local rain_template_medium =StormFox2.DownFall.CreateTemplate(m_rain_medium,true)
|
||||
local rain_template_fog = StormFox2.DownFall.CreateTemplate(m_rain_fog, true)
|
||||
local snow_template = StormFox2.DownFall.CreateTemplate(m_snow, false, false)
|
||||
local snow_template_multi = StormFox2.DownFall.CreateTemplate(m_snow_multi, true)
|
||||
local fog_template = StormFox2.DownFall.CreateTemplate(m_rain, true) -- A "empty" particle that hits the ground, and create a fog particle on-hit.
|
||||
StormFox2.Misc.rain_template = rain_template
|
||||
StormFox2.Misc.rain_template_fog = rain_template_fog
|
||||
StormFox2.Misc.rain_template_medium = rain_template_medium
|
||||
StormFox2.Misc.snow_template = snow_template
|
||||
StormFox2.Misc.snow_template_multi = snow_template_multi
|
||||
StormFox2.Misc.fog_template = fog_template
|
||||
|
||||
--rain_template_medium
|
||||
rain_template_medium:SetFadeIn( true )
|
||||
rain_template_medium:SetSize(20,40)
|
||||
rain_template_medium:SetRenderHeight(800)
|
||||
rain_template_medium:SetAlpha(20)
|
||||
|
||||
--rain_template_fog
|
||||
rain_template_fog:SetFadeIn( true )
|
||||
rain_template_fog:SetSize(150, 600)
|
||||
rain_template_fog:SetRandomAngle(0.15)
|
||||
rain_template_fog:SetSpeed( 0.5 )
|
||||
|
||||
snow_template:SetRandomAngle(0.4)
|
||||
snow_template:SetSpeed( 1 * 0.15)
|
||||
snow_template:SetSize(5,5)
|
||||
snow_template_multi:SetFadeIn( true )
|
||||
snow_template_multi:SetSize(300,300)
|
||||
|
||||
--snow_template_multi:SetRenderHeight( 600 )
|
||||
snow_template_multi:SetRandomAngle(0.3)
|
||||
|
||||
-- Think functions:
|
||||
function rain_template_fog:Think()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local fC = StormFox2.Fog.GetColor()
|
||||
local L = math.min(StormFox2.Weather.GetLuminance(), 100)
|
||||
local TL = StormFox2.Thunder.GetLight() / 2
|
||||
local speed = 0.162 * P + 0.324
|
||||
self:SetColor( Color(fC.r + TL + 15, fC.g + TL + 15, fC.b + TL + 15) )
|
||||
self:SetAlpha( math.min(255, math.max(0, (P - 0.5) * 525 )) )
|
||||
self:SetSpeed( speed )
|
||||
end
|
||||
|
||||
-- Particle Explosion
|
||||
-- Make "rain" explosion at rain particles
|
||||
function rain_template:OnExplosion( vExPos, nDisPercent, iRange, iMagnetide )
|
||||
local e_ang = (self:GetPos() - vExPos):Angle():Forward()
|
||||
local boost = nDisPercent * 5
|
||||
local p = StormFox2.DownFall.AddParticle( "effects/splash1", vExPos + e_ang * iRange *nDisPercent , false )
|
||||
p:SetStartSize(math.random(32, 20))
|
||||
p:SetEndSize(5)
|
||||
p:SetDieTime(2.5)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(6)
|
||||
p:SetGravity( physenv.GetGravity() * 2 )
|
||||
p:SetVelocity( e_ang * iMagnetide * boost)
|
||||
p:SetAirResistance(3)
|
||||
p:SetCollide(true)
|
||||
p:SetRoll(math.random(360))
|
||||
p:SetCollideCallback(function( part )
|
||||
part:SetDieTime(0)
|
||||
end)
|
||||
end
|
||||
rain_template_medium.OnExplosion = rain_template.OnExplosion
|
||||
|
||||
-- Particle Hit
|
||||
function snow_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSnowflake( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
function rain_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
elseif nHitType == SF_DOWNFALL_HIT_GLASS then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
function rain_template_fog:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if math.random(1,3)> 2 then return end
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
local i = 0
|
||||
function snow_template_multi:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
if i < 10 then
|
||||
i = i + 1
|
||||
return
|
||||
end
|
||||
i = 0
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
|
||||
fog_template:SetSize(512,512)
|
||||
fog_template:SetSpeed(5)
|
||||
fog_template:SetAlpha(0)
|
||||
function fog_template:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("stormfox2.postlib", "stormfox2.loadParticles", init)
|
||||
if StormFox2.DownFall and StormFox2.DownFall.CreateTemplate then
|
||||
init()
|
||||
end
|
||||
36
lua/stormfox2/lib/cl_entities.lua
Normal file
36
lua/stormfox2/lib/cl_entities.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Returns an explosion from x position
|
||||
net.Receive("StormFox2.entity.explosion", function(len)
|
||||
local pos = net.ReadVector()
|
||||
local iRadiusOverride = net.ReadInt(16)
|
||||
local iMagnitude = net.ReadUInt(16)
|
||||
hook.Run("StormFox2.Entitys.OnExplosion", pos, iRadiusOverride, iMagnitude)
|
||||
end)
|
||||
|
||||
StormFox2.Ent = {}
|
||||
hook.Add("stormfox2.postlib", "stormfox2.c_ENT",function()
|
||||
StormFox2.Ent.env_skypaints = true -- Always
|
||||
StormFox2.Ent.env_fog_controllers = true -- Always
|
||||
StormFox2.Ent.light_environments = false -- Special
|
||||
for k,v in ipairs( StormFox2.Map.FindClass("light_environment") ) do
|
||||
if v.targetname then
|
||||
StormFox2.Ent.light_environments = true
|
||||
break
|
||||
end
|
||||
end
|
||||
StormFox2.Ent.shadow_controls = #StormFox2.Map.FindClass("shadow_control") > 0
|
||||
StormFox2.Ent.env_tonemap_controllers = #StormFox2.Map.FindClass("env_tonemap_controller") > 0
|
||||
StormFox2.Ent.env_winds = #StormFox2.Map.FindClass("env_wind") > 0
|
||||
StormFox2.Ent.env_tonemap_controller = #StormFox2.Map.FindClass("env_tonemap_controller") > 0
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.c_ENT")
|
||||
end)
|
||||
130
lua/stormfox2/lib/cl_fonts.lua
Normal file
130
lua/stormfox2/lib/cl_fonts.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
surface.CreateFont( "SF_Display_H", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 30,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Display_H2", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 20,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Display_H3", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 14,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Menu_H2", {
|
||||
font = "coolvetica", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 20,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SkyFox-DigitalClock", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 50,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont("SF2.W_Button", {
|
||||
font = "Tahoma",
|
||||
size = 15,
|
||||
weight = 1500,
|
||||
})
|
||||
|
||||
-- Tool
|
||||
surface.CreateFont( "sf_tool_large", {
|
||||
font = "Verdana", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 30,
|
||||
weight = 700,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = true,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "sf_tool_small", {
|
||||
font = "Verdana",
|
||||
size = 17,
|
||||
weight = 1000
|
||||
} )
|
||||
2149
lua/stormfox2/lib/cl_vgui.lua
Normal file
2149
lua/stormfox2/lib/cl_vgui.lua
Normal file
File diff suppressed because it is too large
Load Diff
538
lua/stormfox2/lib/sh_cami.lua
Normal file
538
lua/stormfox2/lib/sh_cami.lua
Normal file
@@ -0,0 +1,538 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
CAMI - Common Admin Mod Interface.
|
||||
Makes admin mods intercompatible and provides an abstract privilege interface
|
||||
for third party addons.
|
||||
|
||||
IMPORTANT: This is a draft script. It is very much WIP.
|
||||
|
||||
Follows the specification on this page:
|
||||
https://docs.google.com/document/d/1QIRVcAgZfAYf1aBl_dNV_ewR6P25wze2KmUVzlbFgMI
|
||||
|
||||
|
||||
Structures:
|
||||
CAMI_USERGROUP, defines the charactaristics of a usergroup:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the usergroup
|
||||
Inherits
|
||||
string
|
||||
The name of the usergroup this usergroup inherits from
|
||||
}
|
||||
|
||||
CAMI_PRIVILEGE, defines the charactaristics of a privilege:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the privilege
|
||||
MinAccess
|
||||
string
|
||||
One of the following three: user/admin/superadmin
|
||||
Description
|
||||
string
|
||||
optional
|
||||
A text describing the purpose of the privilege
|
||||
HasAccess
|
||||
function(
|
||||
privilege :: CAMI_PRIVILEGE,
|
||||
actor :: Player,
|
||||
target :: Player
|
||||
) :: bool
|
||||
optional
|
||||
Function that decides whether a player can execute this privilege,
|
||||
optionally on another player (target).
|
||||
}
|
||||
]]
|
||||
|
||||
-- Version number in YearMonthDay format.
|
||||
local version = 20150902.1
|
||||
|
||||
if CAMI and CAMI.Version >= version then return end
|
||||
|
||||
CAMI = CAMI or {}
|
||||
CAMI.Version = version
|
||||
|
||||
--[[
|
||||
usergroups
|
||||
Contains the registered CAMI_USERGROUP usergroup structures.
|
||||
Indexed by usergroup name.
|
||||
]]
|
||||
local usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {
|
||||
user = {
|
||||
Name = "user",
|
||||
Inherits = "user"
|
||||
},
|
||||
admin = {
|
||||
Name = "admin",
|
||||
Inherits = "user"
|
||||
},
|
||||
superadmin = {
|
||||
Name = "superadmin",
|
||||
Inherits = "admin"
|
||||
}
|
||||
}
|
||||
|
||||
--[[
|
||||
privileges
|
||||
Contains the registered CAMI_PRIVILEGE privilege structures.
|
||||
Indexed by privilege name.
|
||||
]]
|
||||
local privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}
|
||||
|
||||
--[[
|
||||
CAMI.RegisterUsergroup
|
||||
Registers a usergroup with CAMI.
|
||||
|
||||
Parameters:
|
||||
usergroup
|
||||
CAMI_USERGROUP
|
||||
(see CAMI_USERGROUP structure)
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.RegisterUsergroup function and the
|
||||
CAMI.OnUsergroupRegistered hook don't cause an infinite loop
|
||||
|
||||
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
The usergroup given as argument.
|
||||
]]
|
||||
function CAMI.RegisterUsergroup(usergroup, source)
|
||||
usergroups[usergroup.Name] = usergroup
|
||||
|
||||
hook.Call("CAMI.OnUsergroupRegistered", nil, usergroup, source)
|
||||
return usergroup
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterUsergroup
|
||||
Unregisters a usergroup from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the usergroup is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.UnregisterUsergroup function and the
|
||||
CAMI.OnUsergroupUnregistered hook don't cause an infinite loop
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterUsergroup(usergroupName, source)
|
||||
if not usergroups[usergroupName] then return false end
|
||||
|
||||
local usergroup = usergroups[usergroupName]
|
||||
usergroups[usergroupName] = nil
|
||||
|
||||
hook.Call("CAMI.OnUsergroupUnregistered", nil, usergroup, source)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroups
|
||||
Retrieves all registered usergroups.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_USERGROUP, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetUsergroups()
|
||||
return usergroups
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroup
|
||||
Receives information about a usergroup.
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
Returns nil when the usergroup does not exist.
|
||||
]]
|
||||
function CAMI.GetUsergroup(usergroupName)
|
||||
return usergroups[usergroupName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UsergroupInherits
|
||||
Returns true when usergroupName1 inherits usergroupName2.
|
||||
Note that usergroupName1 does not need to be a direct child.
|
||||
Every usergroup trivially inherits itself.
|
||||
|
||||
Parameters:
|
||||
usergroupName1
|
||||
string
|
||||
The name of the usergroup that is queried.
|
||||
usergroupName2
|
||||
string
|
||||
The name of the usergroup of which is queried whether usergroupName1
|
||||
inherits from.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether usergroupName1 inherits usergroupName2.
|
||||
]]
|
||||
function CAMI.UsergroupInherits(usergroupName1, usergroupName2)
|
||||
repeat
|
||||
if usergroupName1 == usergroupName2 then return true end
|
||||
|
||||
usergroupName1 = usergroups[usergroupName1] and
|
||||
usergroups[usergroupName1].Inherits or
|
||||
usergroupName1
|
||||
until not usergroups[usergroupName1] or
|
||||
usergroups[usergroupName1].Inherits == usergroupName1
|
||||
|
||||
-- One can only be sure the usergroup inherits from user if the
|
||||
-- usergroup isn't registered.
|
||||
return usergroupName1 == usergroupName2 or usergroupName2 == "user"
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.InheritanceRoot
|
||||
All usergroups must eventually inherit either user, admin or superadmin.
|
||||
Regardless of what inheritance mechism an admin may or may not have, this
|
||||
always applies.
|
||||
|
||||
This method always returns either user, admin or superadmin, based on what
|
||||
usergroups eventually inherit.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup of which the root of inheritance is
|
||||
requested
|
||||
|
||||
Return value:
|
||||
string
|
||||
The name of the root usergroup (either user, admin or superadmin)
|
||||
]]
|
||||
function CAMI.InheritanceRoot(usergroupName)
|
||||
if not usergroups[usergroupName] then return end
|
||||
|
||||
local inherits = usergroups[usergroupName].Inherits
|
||||
while inherits ~= usergroups[usergroupName].Inherits do
|
||||
usergroupName = usergroups[usergroupName].Inherits
|
||||
end
|
||||
|
||||
return usergroupName
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.RegisterPrivilege
|
||||
Registers a privilege with CAMI.
|
||||
Note: do NOT register all your admin mod's privileges with this function!
|
||||
This function is for third party addons to register privileges
|
||||
with admin mods, not for admin mods sharing the privileges amongst one
|
||||
another.
|
||||
|
||||
Parameters:
|
||||
privilege
|
||||
CAMI_PRIVILEGE
|
||||
See CAMI_PRIVILEGE structure.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE
|
||||
The privilege given as argument.
|
||||
]]
|
||||
function CAMI.RegisterPrivilege(privilege)
|
||||
privileges[privilege.Name] = privilege
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeRegistered", nil, privilege)
|
||||
|
||||
return privilege
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterPrivilege
|
||||
Unregisters a privilege from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the privilege is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterPrivilege(privilegeName)
|
||||
if not privileges[privilegeName] then return false end
|
||||
|
||||
local privilege = privileges[privilegeName]
|
||||
privileges[privilegeName] = nil
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeUnregistered", nil, privilege)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivileges
|
||||
Retrieves all registered privileges.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_PRIVILEGE, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetPrivileges()
|
||||
return privileges
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivilege
|
||||
Receives information about a privilege.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE when the privilege exists.
|
||||
nil when the privilege does not exist.
|
||||
]]
|
||||
function CAMI.GetPrivilege(privilegeName)
|
||||
return privileges[privilegeName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.PlayerHasAccess
|
||||
Queries whether a certain player has the right to perform a certain action.
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorPly
|
||||
Player
|
||||
The player of which is requested whether they have the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
-- Default access handler
|
||||
local defaultAccessHandler = {["CAMI.PlayerHasAccess"] =
|
||||
function(_, actorPly, privilegeName, callback, _, extraInfoTbl)
|
||||
-- The server always has access in the fallback
|
||||
if not IsValid(actorPly) then return callback(true, "Fallback.") end
|
||||
|
||||
local priv = privileges[privilegeName]
|
||||
|
||||
local fallback = extraInfoTbl and (
|
||||
not extraInfoTbl.Fallback and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "user" and true or
|
||||
extraInfoTbl.Fallback == "admin" and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "superadmin" and actorPly:IsSuperAdmin())
|
||||
|
||||
|
||||
if not priv then return callback(fallback, "Fallback.") end
|
||||
|
||||
callback(
|
||||
priv.MinAccess == "user" or
|
||||
priv.MinAccess == "admin" and actorPly:IsAdmin() or
|
||||
priv.MinAccess == "superadmin" and actorPly:IsSuperAdmin()
|
||||
, "Fallback.")
|
||||
end,
|
||||
["CAMI.SteamIDHasAccess"] =
|
||||
function(_, _, _, callback)
|
||||
callback(false, "No information available.")
|
||||
end
|
||||
}
|
||||
function CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
hook.Call("CAMI.PlayerHasAccess", defaultAccessHandler, actorPly,
|
||||
privilegeName, callback, targetPly, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPlayersWithAccess
|
||||
Finds the list of currently joined players who have the right to perform a
|
||||
certain action.
|
||||
NOTE: this function will NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(players)
|
||||
This function will be called with the list of players with access.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
]]
|
||||
function CAMI.GetPlayersWithAccess(privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
local allowedPlys = {}
|
||||
local allPlys = player.GetAll()
|
||||
local countdown = #allPlys
|
||||
|
||||
local function onResult(ply, hasAccess, _)
|
||||
countdown = countdown - 1
|
||||
|
||||
if hasAccess then table.insert(allowedPlys, ply) end
|
||||
if countdown == 0 then callback(allowedPlys) end
|
||||
end
|
||||
|
||||
for _, ply in pairs(allPlys) do
|
||||
CAMI.PlayerHasAccess(ply, privilegeName,
|
||||
function(...) onResult(ply, ...) end,
|
||||
targetPly, extraInfoTbl)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SteamIDHasAccess
|
||||
Queries whether a player with a steam ID has the right to perform a certain
|
||||
action.
|
||||
Note: the player does not need to be in the server for this to
|
||||
work.
|
||||
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorSteam
|
||||
Player
|
||||
The SteamID of the player of which is requested whether they have
|
||||
the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetSteam
|
||||
Optional.
|
||||
The SteamID of the player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
function CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,
|
||||
targetSteam, extraInfoTbl)
|
||||
hook.Call("CAMI.SteamIDHasAccess", defaultAccessHandler, actorSteam,
|
||||
privilegeName, callback, targetSteam, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a player. This
|
||||
function communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
Player
|
||||
The player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalUserGroupChanged(ply, old, new, source)
|
||||
hook.Call("CAMI.PlayerUsergroupChanged", nil, ply, old, new, source)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalSteamIDUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a disconnected
|
||||
player. This communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
string
|
||||
The steam ID of the player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalSteamIDUserGroupChanged(steamId, old, new, source)
|
||||
hook.Call("CAMI.SteamIDUsergroupChanged", nil, steamId, old, new, source)
|
||||
end
|
||||
228
lua/stormfox2/lib/sh_data.lua
Normal file
228
lua/stormfox2/lib/sh_data.lua
Normal file
@@ -0,0 +1,228 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Unlike SF1, this doesn't support networking.
|
||||
|
||||
If timespeed changes, and some lerpvalues like temperature has been applied, we need to keep it syncronised.
|
||||
That is why we now use Time value instead of CurTime
|
||||
|
||||
Data.Set( sKey, zVar, nDelta ) Sets the data. Supports lerping if given delta.
|
||||
Data.Get( sKey, zDefault ) Returns the data. Returns zDefault if nil.
|
||||
Data.GetFinal( zKey, zDefault) Returns the data without calculating the lerp.
|
||||
Data.IsLerping( sKey ) Returns true if the data is currently lerping.
|
||||
|
||||
Hooks:
|
||||
- StormFox2.data.change sKey zVar Called when data changed or started lerping.
|
||||
- StormFox2.data.lerpstart sKey zVar Called when data started lerping
|
||||
- StormFox2.data.lerpend sKey zVar Called when data stopped lerping (This will only be called if we check for the variable)
|
||||
]]
|
||||
StormFox2.Data = {}
|
||||
|
||||
StormFox_DATA = {} -- Var
|
||||
StormFox_AIMDATA = {} -- Var, start, end
|
||||
|
||||
--[[TODO: There are still problems with nil varables.
|
||||
]]
|
||||
|
||||
---Returns the final data, ignoring lerp. Will return zDefault as fallback.
|
||||
---@param sKey string
|
||||
---@param zDefault any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Data.GetFinal( sKey, zDefault )
|
||||
if StormFox_AIMDATA[sKey] then
|
||||
if StormFox_AIMDATA[sKey][1] ~= nil then
|
||||
return StormFox_AIMDATA[sKey][1]
|
||||
else
|
||||
return zDefault
|
||||
end
|
||||
end
|
||||
if StormFox_DATA[sKey] ~= nil then
|
||||
return StormFox_DATA[sKey]
|
||||
end
|
||||
return zDefault
|
||||
end
|
||||
|
||||
local lerpCache = {}
|
||||
local function calcFraction(start_cur, end_cur)
|
||||
local n = CurTime()
|
||||
if n >= end_cur then return 1 end
|
||||
local d = end_cur - start_cur
|
||||
return (n - start_cur) / d
|
||||
end
|
||||
do
|
||||
local function isColor(t)
|
||||
if type(t) ~= "table" then return false end
|
||||
return t.r and t.g and t.b and true or false
|
||||
end
|
||||
local function LerpVar(fraction, from, to)
|
||||
local t = type(from)
|
||||
if t ~= type(to) then
|
||||
--StormFox2.Warning("Can't lerp " .. type(from) .. " to " .. type(to) .. "!")
|
||||
return to
|
||||
end
|
||||
if t == "number" then
|
||||
return Lerp(fraction, from, to)
|
||||
elseif t == "string" then
|
||||
return fraction > .5 and to or from
|
||||
elseif isColor(from) then
|
||||
local r = Lerp(fraction, from.r, to.r)
|
||||
local g = Lerp(fraction, from.g, to.g)
|
||||
local b = Lerp(fraction, from.b, to.b)
|
||||
local a = Lerp(fraction, from.a, to.a)
|
||||
return Color(r,g,b,a)
|
||||
elseif t == "vector" then
|
||||
return LerpVector(fraction, from, to)
|
||||
elseif t == "angle" then
|
||||
return LerpAngle(fraction, from, to)
|
||||
elseif t == "boolean" then
|
||||
if fraction > .5 then
|
||||
return to
|
||||
else
|
||||
return from
|
||||
end
|
||||
else
|
||||
--print("UNKNOWN", t,"TO",to)
|
||||
end
|
||||
end
|
||||
|
||||
---Returns data. Will return zDefault as fallback.
|
||||
---@param sKey string
|
||||
---@param zDefault any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Data.Get( sKey, zDefault )
|
||||
-- Check if lerping
|
||||
local var1 = StormFox_DATA[sKey]
|
||||
if not StormFox_AIMDATA[sKey] then
|
||||
if var1 ~= nil then
|
||||
return var1
|
||||
else
|
||||
return zDefault
|
||||
end
|
||||
end
|
||||
-- Check cache and return
|
||||
if lerpCache[sKey] ~= nil then return lerpCache[sKey] end
|
||||
-- Calc
|
||||
local fraction = calcFraction(StormFox_AIMDATA[sKey][2],StormFox_AIMDATA[sKey][3])
|
||||
local var2 = StormFox_AIMDATA[sKey][1]
|
||||
if fraction <= 0 then
|
||||
return var1
|
||||
elseif fraction < 1 then
|
||||
lerpCache[sKey] = LerpVar( fraction, var1, var2 )
|
||||
if not lerpCache[sKey] then
|
||||
--print("DATA",sKey, zDefault)
|
||||
--print(debug.traceback())
|
||||
end
|
||||
return lerpCache[sKey] or zDefault
|
||||
else -- Fraction end
|
||||
StormFox_DATA[sKey] = var2
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
hook.Run("StormFox2.data.lerpend",sKey,var2)
|
||||
return var2 or zDefault
|
||||
end
|
||||
end
|
||||
local n = 0
|
||||
-- Reset cache after 4 frames
|
||||
hook.Add("Think", "StormFox2.resetdatalerp", function()
|
||||
n = n + 1
|
||||
if n < 4 then return end
|
||||
n = 0
|
||||
lerpCache = {}
|
||||
end)
|
||||
end
|
||||
|
||||
---Sets data. Will lerp if given delta time. Use StormFox2.Network.Set if you want want to network it.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@shared
|
||||
function StormFox2.Data.Set( sKey, zVar, nDelta )
|
||||
-- Check if vars are the same
|
||||
if StormFox_DATA[sKey] ~= nil and not StormFox_AIMDATA[sKey] then
|
||||
if StormFox_DATA[sKey] == zVar then return end
|
||||
end
|
||||
-- If time is paused, there shouldn't be any lerping
|
||||
if StormFox2.Time and StormFox2.Time.IsPaused and StormFox2.Time.IsPaused() then
|
||||
nDelta = 0
|
||||
end
|
||||
-- Delete old cache
|
||||
lerpCache[sKey] = nil
|
||||
-- Set to nil
|
||||
if not zVar and zVar == nil then
|
||||
StormFox_DATA[sKey] = nil
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
return
|
||||
end
|
||||
-- If delta is 0 or below. (Or no prev data). Set it.
|
||||
if not nDelta or nDelta <= 0 or StormFox_DATA[sKey] == nil or StormFox2.Time.GetSpeed_RAW() <= 0 then
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
StormFox_DATA[sKey] = zVar
|
||||
hook.Run("StormFox2.data.change",sKey,zVar)
|
||||
return
|
||||
end
|
||||
-- Get the current lerping value and set that as a start
|
||||
if StormFox_AIMDATA[sKey] then
|
||||
StormFox_DATA[sKey] = StormFox2.Data.Get( sKey )
|
||||
end
|
||||
StormFox_AIMDATA[sKey] = {zVar, CurTime(), CurTime() + nDelta, StormFox2.Time.GetSpeed_RAW()}
|
||||
hook.Run("StormFox2.data.lerpstart",sKey,zVar, nDelta)
|
||||
hook.Run("StormFox2.data.change", sKey, zVar, nDelta)
|
||||
end
|
||||
|
||||
---Returns true if the value is currently lerping.
|
||||
---@param sKey string
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Data.IsLerping( sKey )
|
||||
if not StormFox_AIMDATA[sKey] then return false end
|
||||
-- Check and see if we're done lerping
|
||||
local fraction = calcFraction(StormFox_AIMDATA[sKey][2],StormFox_AIMDATA[sKey][3])
|
||||
if fraction < 1 then
|
||||
return true
|
||||
end
|
||||
-- We're done lerping.
|
||||
StormFox_DATA[sKey] = StormFox2.Data.GetFinal( sKey )
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
lerpCache[sKey] = nil
|
||||
hook.Run("StormFox2.data.lerpend",sKey,zVar)
|
||||
return true
|
||||
end
|
||||
|
||||
---Returns a CurTime for when the data is done lerping.
|
||||
---@param sKey string
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Data.GetLerpEnd( sKey )
|
||||
if not StormFox_AIMDATA[sKey] then return 0 end
|
||||
return StormFox_AIMDATA[sKey][3]
|
||||
end
|
||||
|
||||
-- If time changes, we need to update the lerp values
|
||||
hook.Add("StormFox2.Time.Changed", "StormFox2.datatimefix", function()
|
||||
local nT = StormFox2.Time.GetSpeed_RAW()
|
||||
local c = CurTime()
|
||||
if nT <= 0.001 then return end
|
||||
for k,v in pairs( StormFox_AIMDATA ) do
|
||||
if not v[4] or v[4] == nT then continue end
|
||||
local now_value = StormFox2.Data.Get( k )
|
||||
if not StormFox_AIMDATA[k] then continue end -- After checking the value, it is now gone.
|
||||
if now_value then
|
||||
StormFox_DATA[k] = now_value
|
||||
end
|
||||
local delta_timeamount = (v[3] - c) -- Time left
|
||||
local delta_time = v[4] / nT -- Time multiplication
|
||||
StormFox_AIMDATA[k][2] = c
|
||||
StormFox_AIMDATA[k][3] = c + delta_timeamount * delta_time
|
||||
StormFox_AIMDATA[k][4] = nT
|
||||
end
|
||||
end)
|
||||
1006
lua/stormfox2/lib/sh_downfall.lua
Normal file
1006
lua/stormfox2/lib/sh_downfall.lua
Normal file
File diff suppressed because it is too large
Load Diff
1040
lua/stormfox2/lib/sh_mapglass.lua
Normal file
1040
lua/stormfox2/lib/sh_mapglass.lua
Normal file
File diff suppressed because it is too large
Load Diff
90
lua/stormfox2/lib/sh_network.lua
Normal file
90
lua/stormfox2/lib/sh_network.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Network.Set( sKey, zVar, nDelta ) Same as Data.Set( sKey, zVar, nDelta ) but networks it to all clients.
|
||||
]]
|
||||
StormFox2.Network = {}
|
||||
StormFox_NETWORK = {} -- Var
|
||||
|
||||
if SERVER then
|
||||
local tickets = {}
|
||||
-- Forces a client to recive the data
|
||||
function StormFox2.Network.ForceUpdate( ply )
|
||||
tickets[ply] = true
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(false)
|
||||
net.WriteTable(StormFox_NETWORK)
|
||||
net.Send(ply)
|
||||
hook.Run("StormFox2.data.initspawn", ply)
|
||||
end
|
||||
|
||||
---Same as StormFox2.Data.Set, but networks it to all clients.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@server
|
||||
function StormFox2.Network.Set( sKey, zVar, nDelta )
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
if StormFox_NETWORK[sKey] == zVar then return end
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(true)
|
||||
net.WriteString(sKey)
|
||||
net.WriteType(zVar)
|
||||
net.WriteUInt(nDelta or 0, 16)
|
||||
net.Broadcast()
|
||||
StormFox_NETWORK[sKey] = zVar
|
||||
end
|
||||
|
||||
---Force-set the data, ignoring cache.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@server
|
||||
function StormFox2.Network.ForceSet( sKey, zVar, nDelta )
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(true)
|
||||
net.WriteString(sKey)
|
||||
net.WriteType(zVar)
|
||||
net.WriteUInt(nDelta or 0, 16)
|
||||
net.Broadcast()
|
||||
StormFox_NETWORK[sKey] = zVar
|
||||
end
|
||||
net.Receive(StormFox2.Net.Network, function(len, ply)
|
||||
if tickets[ply] then return end
|
||||
tickets[ply] = true
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(false)
|
||||
net.WriteTable(StormFox_NETWORK)
|
||||
net.Send(ply)
|
||||
hook.Run("StormFox2.data.initspawn", ply)
|
||||
end)
|
||||
else
|
||||
net.Receive(StormFox2.Net.Network, function(len)
|
||||
if net.ReadBool() then
|
||||
local sKey = net.ReadString()
|
||||
local zVar = net.ReadType()
|
||||
local nDelta = net.ReadUInt(16)
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
else
|
||||
StormFox_NETWORK = net.ReadTable()
|
||||
for k,v in pairs(StormFox_NETWORK) do
|
||||
StormFox2.Data.Set(k, v)
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- Ask the server what data we have
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.network", function()
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
164
lua/stormfox2/lib/sh_permission.lua
Normal file
164
lua/stormfox2/lib/sh_permission.lua
Normal file
@@ -0,0 +1,164 @@
|
||||
--[[
|
||||
| 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.Permission = {}
|
||||
|
||||
hook.Add("stormfox2.postlib", "stormfox2.privileges", function()
|
||||
if not CAMI then return end
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "StormFox Settings",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
-- Permission to edit StormFox weather and time
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "StormFox WeatherEdit",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
end)
|
||||
|
||||
local SF_SERVEREDIT = 0
|
||||
local SF_WEATHEREDIT= 1
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("StormFox2.menu")
|
||||
-- "Fake" settings
|
||||
local commands = {
|
||||
["cvslist"] = function( var )
|
||||
StormFox2.Setting.SetCVS( tostring( var ) )
|
||||
end
|
||||
}
|
||||
net.Receive("StormFox2.menu", function(len, ply)
|
||||
local req = net.ReadBool()
|
||||
if ply:IsListenServerHost() or game.SinglePlayer() then
|
||||
net.Start("StormFox2.menu")
|
||||
net.WriteBool(req)
|
||||
net.Send( ply )
|
||||
return
|
||||
end
|
||||
CAMI.PlayerHasAccess(ply,req and "StormFox Settings" or "StormFox WeatherEdit",function(b)
|
||||
if not b then return end
|
||||
net.Start("StormFox2.menu")
|
||||
net.WriteBool(req)
|
||||
net.Send( ply )
|
||||
end)
|
||||
end)
|
||||
local function NoAccess(ply, msg)
|
||||
if not ply then
|
||||
MsgC( Color(155,155,255),"[StormFox2] ", color_white, msg )
|
||||
MsgN()
|
||||
return
|
||||
end
|
||||
net.Start( StormFox2.Net.Permission )
|
||||
net.WriteString(msg)
|
||||
net.Send(ply)
|
||||
end
|
||||
local function plyRequestSetting(ply, convar, var)
|
||||
if not CAMI then return end
|
||||
-- Check if its a stormfox setting
|
||||
local obj = StormFox2.Setting.GetObject( convar ) or commands[ convar ]
|
||||
if not obj then
|
||||
if ply then
|
||||
NoAccess(ply, "Invalid setting: " .. tostring(convar))
|
||||
end
|
||||
return false, "Not SF"
|
||||
end
|
||||
-- If singleplayer/host
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then
|
||||
if type(obj) == "function" then
|
||||
obj( var )
|
||||
else
|
||||
obj:SetValue( var )
|
||||
end
|
||||
return
|
||||
end
|
||||
-- Check CAMI
|
||||
CAMI.PlayerHasAccess(ply,"StormFox Settings",function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the settings!")
|
||||
return
|
||||
end
|
||||
if type(obj) == "function" then
|
||||
obj( var )
|
||||
else
|
||||
obj:SetValue( var )
|
||||
end
|
||||
end)
|
||||
end
|
||||
local function plyRequestEdit( ply, tID, var)
|
||||
if not CAMI then return end
|
||||
-- If singleplayer/host
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then
|
||||
return StormFox2.Menu.SetWeatherData(ply, tID, var)
|
||||
end
|
||||
-- Check CAMI
|
||||
CAMI.PlayerHasAccess(ply,"StormFox WeatherEdit",function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the weather!")
|
||||
return
|
||||
end
|
||||
StormFox2.Menu.SetWeatherData(ply, tID, var)
|
||||
end)
|
||||
end
|
||||
net.Receive( StormFox2.Net.Permission, function(len, ply)
|
||||
local t = net.ReadUInt(1)
|
||||
if t == SF_SERVEREDIT then
|
||||
plyRequestSetting(ply, net.ReadString(), net.ReadType())
|
||||
elseif t == SF_WEATHEREDIT then
|
||||
plyRequestEdit(ply, net.ReadUInt(4), net.ReadType())
|
||||
end
|
||||
end)
|
||||
|
||||
---Asks CAMI if the user has access to said permission. Will call and return onSuccess if they do.
|
||||
---@param ply Player
|
||||
---@param sPermission string
|
||||
---@param onSuccess function
|
||||
---@param ... any
|
||||
---@return any|nil
|
||||
---@server
|
||||
function StormFox2.Permission.EditAccess(ply, sPermission, onSuccess, ...)
|
||||
if not ply or ply:IsListenServerHost() then -- Console or host
|
||||
return onSuccess(ply, ... )
|
||||
end
|
||||
local a = {...}
|
||||
CAMI.PlayerHasAccess(ply,sPermission,function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the weather.")
|
||||
return
|
||||
end
|
||||
onSuccess(ply, unpack(a) )
|
||||
end)
|
||||
end
|
||||
else
|
||||
net.Receive(StormFox2.Net.Permission, function(len)
|
||||
local str = net.ReadString()
|
||||
chat.AddText(Color(155,155,255),"[StormFox2] ", color_white, str)
|
||||
end)
|
||||
net.Receive("StormFox2.menu", function(len)
|
||||
local n = net.ReadBool()
|
||||
if n then
|
||||
StormFox2.Menu._OpenSV()
|
||||
else
|
||||
StormFox2.Menu._OpenController()
|
||||
end
|
||||
end)
|
||||
|
||||
---Asks the server to change a setting.
|
||||
---@param convar string
|
||||
---@param var any
|
||||
---@client
|
||||
function StormFox2.Permission.RequestSetting( convar, var )
|
||||
net.Start(StormFox2.Net.Permission)
|
||||
net.WriteUInt(SF_SERVEREDIT, 1)
|
||||
net.WriteString( convar )
|
||||
net.WriteType(var)
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
22
lua/stormfox2/lib/sh_reset.lua
Normal file
22
lua/stormfox2/lib/sh_reset.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Allows to reset
|
||||
if _STORMFOX_POSTENTITY then
|
||||
timer.Simple(2, function()
|
||||
hook.Run("StormFox2.InitPostEntity")
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("InitPostEntity", "SF_PostEntity", function()
|
||||
hook.Run("StormFox2.InitPostEntity")
|
||||
_STORMFOX_POSTENTITY = true
|
||||
end)
|
||||
815
lua/stormfox2/lib/sh_settings.lua
Normal file
815
lua/stormfox2/lib/sh_settings.lua
Normal file
@@ -0,0 +1,815 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
StormFox Settings
|
||||
Handle settings and convert convars.
|
||||
|
||||
- Hooks: StormFox2.Setting.Change sName, vVarable
|
||||
---------------------------------------------------------------------------]]
|
||||
StormFox2.Setting = {}
|
||||
-- Local functions and var
|
||||
|
||||
local NET_ALLSETTINGS = 0
|
||||
local NET_UPDATE = 1
|
||||
|
||||
local settingCallback = {}
|
||||
local settings = {}
|
||||
local cache = {}
|
||||
|
||||
local ValueToString, StringToValue
|
||||
do
|
||||
function StringToValue( str, _type )
|
||||
_type = _type:lower()
|
||||
if ( _type == "vector" ) then return Vector( str ) end
|
||||
if ( _type == "angle" ) then return Angle( str ) end
|
||||
if ( _type == "float" or _type == "number" ) then return tonumber( str ) end
|
||||
if ( _type == "int" ) then return math.Round( tonumber( str ) ) end
|
||||
if ( _type == "bool" or _type == "boolean" ) then return tobool( str ) end
|
||||
if ( _type == "string" ) then return tostring( str ) end
|
||||
if ( _type == "entity" ) then return Entity( str ) end
|
||||
StormFox2.Warning("Unable parse: " .. _type, true)
|
||||
end
|
||||
function ValueToString( var, _type )
|
||||
_type = (_type or type( var )):lower()
|
||||
if _type == "vector" or _type == "angle" then return string.format( "%.2f %.2f %.2f", var:Unpack() ) end
|
||||
if _type == "number" or _type == "float" then return util.NiceFloat( var ) end
|
||||
if _type == "int" then var = math.Round( var ) end
|
||||
return tostring( var )
|
||||
end
|
||||
end
|
||||
-- Load the settings
|
||||
local mapFile, defaultFile
|
||||
if SERVER then
|
||||
mapFile, defaultFile = "stormfox2/sv_settings/" .. game.GetMap() .. ".json", "stormfox2/sv_settings/default.json"
|
||||
else
|
||||
mapFile, defaultFile = "stormfox2/cl_settings/" .. game.GetMap() .. ".json", "stormfox2/cl_settings/default.json"
|
||||
end
|
||||
local settingsFile = file.Exists(mapFile, "DATA") and mapFile or defaultFile
|
||||
local fileData = {}
|
||||
if file.Exists(settingsFile, "DATA") then
|
||||
fileData = util.JSONToTable( file.Read(settingsFile, "DATA") or "" ) or {}
|
||||
end
|
||||
local blockSaveFile = false
|
||||
local function saveToFile()
|
||||
if blockSaveFile then return end
|
||||
local data = {}
|
||||
for sName, obj in pairs( settings ) do
|
||||
if CLIENT and obj:IsServer() then continue end -- If you're the client, ignore server settings
|
||||
if sName == "mapfile" then continue end
|
||||
if sName == "mapfile_cl" then continue end
|
||||
if obj:IsDefault() then continue end
|
||||
data[sName] = obj:GetString()
|
||||
end
|
||||
StormFox2.FileWrite( settingsFile, util.TableToJSON(data, true) )
|
||||
end
|
||||
|
||||
---Returns faæse if we're saving to the default json file.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.IsUsingMapFile()
|
||||
return settingsFile == mapFile
|
||||
end
|
||||
|
||||
---Enable/Disable saving to map-specific file.
|
||||
---@param bool boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.UseMapFile( bool )
|
||||
if bool then
|
||||
if settingsFile == mapFile then return end
|
||||
settingsFile = mapFile
|
||||
-- Settings only save once callback is done. Therefor we force this to save
|
||||
local ob = blockSaveFile
|
||||
blockSaveFile = false
|
||||
saveToFile() -- "Copy" the settings to the file
|
||||
blockSaveFile = ob
|
||||
else
|
||||
if settingsFile == defaultFile then return end
|
||||
file.Delete(mapFile)
|
||||
settingsFile = defaultFile
|
||||
-- Reload the default file
|
||||
fileData = util.JSONToTable( file.Read(settingsFile, "DATA") or "" ) or {}
|
||||
blockSaveFile = true
|
||||
for sName, var in pairs( fileData ) do
|
||||
local obj = StormFox2.Setting.GetObject( sName )
|
||||
if not obj then continue end
|
||||
local newVar = fileData[sName] and StringToValue(fileData[sName], obj.type)
|
||||
obj:SetValue( newVar )
|
||||
end
|
||||
blockSaveFile = false
|
||||
end
|
||||
end
|
||||
|
||||
---Forces SF2 to save the settings.
|
||||
---@shared
|
||||
function StormFox2.Setting.ForceSave()
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
end
|
||||
|
||||
---Returns the file we're saving to.
|
||||
---@return string
|
||||
function StormFox2.Setting.GetSaveFile()
|
||||
return settingsFile
|
||||
end
|
||||
|
||||
-- Meta Table
|
||||
|
||||
---@class SF2Convar
|
||||
---@field SetGroup function
|
||||
---@field GetGroup function
|
||||
---@field SetDescription function
|
||||
---@field GetDescription function
|
||||
local meta = {}
|
||||
meta.__index = meta
|
||||
AccessorFunc(meta, "group", "Group")
|
||||
AccessorFunc(meta, "desc", "Description")
|
||||
function meta:GetName()
|
||||
return self.sName
|
||||
end
|
||||
function meta:GetValue()
|
||||
return self.value
|
||||
end
|
||||
function meta:GetDescription()
|
||||
return self.desc
|
||||
end
|
||||
function meta:IsSecret()
|
||||
return self.isSecret or false
|
||||
end
|
||||
function meta:SetValue( var )
|
||||
if self:GetValue() == var then return self end -- Ignore
|
||||
StormFox2.Setting.Set(self:GetName(), var)
|
||||
return self
|
||||
end
|
||||
function meta:GetDefault()
|
||||
return self.default
|
||||
end
|
||||
function meta:IsDefault()
|
||||
return self:GetValue() == self:GetDefault()
|
||||
end
|
||||
function meta:Revert()
|
||||
self:SetValue( self:GetDefault() )
|
||||
end
|
||||
function meta:IsServer()
|
||||
return self.server
|
||||
end
|
||||
function meta:GetType()
|
||||
return self.type
|
||||
end
|
||||
function meta:SetMenuType( sType, tSortOrter )
|
||||
StormFox2.Setting.SetType( self:GetName(), sType, tSortOrter )
|
||||
return self
|
||||
end
|
||||
function meta:GetMin()
|
||||
return self.min
|
||||
end
|
||||
function meta:GetMax()
|
||||
return self.max
|
||||
end
|
||||
function meta:GetString()
|
||||
return ValueToString(self:GetValue(), self:GetType())
|
||||
end
|
||||
function meta:IsFuzzyOn()
|
||||
if not self:GetValue() then return false end
|
||||
if self.type == "number" then
|
||||
if self:GetValue() <= ( self:GetMin() or 0 ) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
function meta:SetFuzzyOn()
|
||||
if self.type == "boolean" then
|
||||
self:SetValue( true )
|
||||
elseif self.type == "number" then
|
||||
local lowest = ( self:GetMin() or 0 )
|
||||
self:SetValue( lowest + 1 )
|
||||
end
|
||||
return self
|
||||
end
|
||||
function meta:SetFuzzyOff()
|
||||
if self.type == "boolean" then
|
||||
self:SetValue( false )
|
||||
elseif self.type == "number" then
|
||||
local lowest = ( self:GetMin() or 0 )
|
||||
self:SetValue( lowest )
|
||||
end
|
||||
return self
|
||||
end
|
||||
function meta:SetFromString( str, type )
|
||||
self:SetValue( StringToValue( str, type or self.type ) )
|
||||
return self
|
||||
end
|
||||
function meta:AddCallback(fFunc,sID)
|
||||
StormFox2.Setting.Callback(self:GetName(),fFunc,sID)
|
||||
end
|
||||
-- Ties the setting to others. Does require all to be booleans or form of toggles
|
||||
do
|
||||
local radioTab = {}
|
||||
local radioTabDefault = {}
|
||||
local blockLoop = false
|
||||
local function callBack(vVar, oldVar, sName, id)
|
||||
if blockLoop then return end -- Another setting made you change. Don't run.
|
||||
local obj = settings[sName]
|
||||
if not obj then StormFox2.Warning("Invalid radio-setting!", true) end
|
||||
local a = radioTab[sName]
|
||||
if not a then return end
|
||||
-- Make sure we turned "on"
|
||||
if not obj:IsFuzzyOn() then -- We got turned off. Make sure at least one is on
|
||||
if not blockLoop then -- Turned off, and we didn't get called by others
|
||||
local default = a[1] -- First one in list. This is to ensure self never get set.
|
||||
for _, other in ipairs( a ) do
|
||||
if other:IsFuzzyOn() then return end -- One of the others are on. Ignore.
|
||||
if radioTabDefault[other:GetName()] then -- I'm the default one
|
||||
default = other
|
||||
end
|
||||
end
|
||||
-- All other settings are off. Try and switch the default on.
|
||||
if default:GetName() == sName then -- Tell the settings we can't be turned off
|
||||
return false
|
||||
else
|
||||
default:SetFuzzyOn()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
blockLoop = true
|
||||
-- Tell the others we turned on, and they have to turn off
|
||||
for _, other in ipairs( a ) do
|
||||
if other:GetName() == obj:GetName() then continue end -- Just to make sure we don't loop around
|
||||
other:SetFuzzyOff()
|
||||
end
|
||||
blockLoop = false
|
||||
end
|
||||
local callOthers = true
|
||||
-- Makes all settings turn off, if one of them are turned on.
|
||||
function meta:SetRadioAll( ... )
|
||||
if self:IsServer() and CLIENT then -- Not your job to keep track.
|
||||
self._radioB = true
|
||||
for _, other in ipairs( { ... } ) do
|
||||
other._radioB = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
local a = {}
|
||||
-- Make sure the arguments doesn't contain itself and all is from the same realm.
|
||||
local f = { ... }
|
||||
for _, other in ipairs( f ) do
|
||||
if other:GetName() == self:GetName() then continue end -- Don't include self
|
||||
if other:IsServer() ~= self:IsServer() then -- Make sure same realm
|
||||
StormFox2.Warning(other:GetName() .. " tried to tie itself to a setting from another realm!")
|
||||
continue
|
||||
end
|
||||
table.insert(a, other)
|
||||
end
|
||||
if #a < 1 then StormFox2.Warning(self:GetName() .. " tried to tie itself to nothing!",true) end
|
||||
-- Tell the other settings to do the same
|
||||
if callOthers then
|
||||
callOthers = false
|
||||
for _, other in ipairs( a ) do
|
||||
other:SetRadio( self, unpack( a ) )
|
||||
end
|
||||
callOthers = true
|
||||
end
|
||||
radioTab[self:GetName()] = a
|
||||
StormFox2.Setting.Callback(self:GetName(),callBack,"radio_setting")
|
||||
return self
|
||||
end
|
||||
function meta:SetRadioDefault() -- Turn on if all others are off
|
||||
radioTabDefault[self:GetName()] = true
|
||||
return self
|
||||
end
|
||||
function meta:IsRadio()
|
||||
return (radioTab[self:GetName()] or self._radioB) and true or false
|
||||
end
|
||||
-- Tells these settings to turn off, if this setting is turned on
|
||||
function meta:SetRadio( ... )
|
||||
if self:IsServer() and CLIENT then -- Not your job to keep track.
|
||||
self._radioB = true
|
||||
return self
|
||||
end
|
||||
local a = {}
|
||||
-- Make sure the arguments doesn't contain itself and all is from the same realm.
|
||||
for _, other in ipairs( { ... } ) do
|
||||
if other:GetName() == self:GetName() then continue end -- Don't include self
|
||||
if other:IsServer() ~= self:IsServer() then -- Make sure same realm
|
||||
StormFox2.Warning(other:GetName() .. " tried to tie itself to a setting from another realm!")
|
||||
continue
|
||||
end
|
||||
table.insert(a, other)
|
||||
end
|
||||
if #a < 1 then StormFox2.Warning(self:GetName() .. " tried to tie itself to nothing!",true) end
|
||||
radioTab[self:GetName()] = a
|
||||
StormFox2.Setting.Callback(self:GetName(),callBack,"radio_setting")
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
local postSettingChace = {}
|
||||
-- Creates a setting and returns the setting-object
|
||||
|
||||
---Creates a server-side setting. Has to be called on the client to show up in the menu.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@param sDescription? string
|
||||
---@param sGroup? string
|
||||
---@param nMin? number
|
||||
---@param nMax? number
|
||||
---@return SF2Convar
|
||||
---@shared
|
||||
function StormFox2.Setting.AddSV(sName,vDefaultVar,sDescription,sGroup, nMin, nMax)
|
||||
if settings[sName] then return settings[sName] end -- Already created
|
||||
if StormFox2.Map then
|
||||
vDefaultVar = StormFox2.Map.GetSetting( sName ) or vDefaultVar
|
||||
end
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t.sName = sName
|
||||
t.type = type(vDefaultVar)
|
||||
if SERVER then
|
||||
if fileData[sName] ~= nil then
|
||||
t.value = StringToValue(fileData[sName], t.type)
|
||||
end
|
||||
if t.value == nil then -- Check convar before setting the setting.
|
||||
local con = GetConVar("sf_" .. sName)
|
||||
if con then
|
||||
t.value = StringToValue(con:GetString(), t.type)
|
||||
end
|
||||
end
|
||||
if t.value == nil then -- If all fails, use the default
|
||||
t.value = vDefaultVar
|
||||
end
|
||||
else
|
||||
t.value = postSettingChace[sName] or vDefaultVar
|
||||
end
|
||||
t.default = vDefaultVar
|
||||
t.server = true
|
||||
t.min = nMin
|
||||
t.max = nMax
|
||||
t:SetGroup( sGroup )
|
||||
t:SetDescription( sDescription )
|
||||
settings[sName] = t
|
||||
return t
|
||||
end
|
||||
|
||||
-- Creates a setting and returns the setting-object
|
||||
if CLIENT then
|
||||
---Creates a client-side setting.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@param sDescription? string
|
||||
---@param sGroup? string
|
||||
---@param nMin? number
|
||||
---@param nMax? number
|
||||
---@return SF2Convar
|
||||
---@client
|
||||
function StormFox2.Setting.AddCL(sName,vDefaultVar,sDescription,sGroup, nMin, nMax)
|
||||
if settings[sName] then return settings[sName] end -- Already added
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t.sName = sName
|
||||
t.type = type(vDefaultVar)
|
||||
if CLIENT then
|
||||
if fileData[sName] ~= nil then
|
||||
t.value = StringToValue(fileData[sName], t.type)
|
||||
end
|
||||
if t.value == nil then
|
||||
local con = GetConVar("sf_" .. sName)
|
||||
if con then
|
||||
t.value = StringToValue(con:GetString(), t.type)
|
||||
end
|
||||
end
|
||||
if t.value == nil then -- If all fails, use the default
|
||||
t.value = vDefaultVar
|
||||
end
|
||||
end
|
||||
t.default = vDefaultVar
|
||||
t.server = false
|
||||
t.min = nMin
|
||||
t.max = nMax
|
||||
t:SetGroup( sGroup )
|
||||
t:SetDescription( sDescription )
|
||||
settings[sName] = t
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
---Tries to onvert to the given defaultvar to match the setting.
|
||||
---@param sName string
|
||||
---@param sString string
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.StringToType( sName, sString )
|
||||
local obj = settings[sName]
|
||||
if not obj then return sString end -- No idea
|
||||
return StringToValue( sString, obj.type )
|
||||
end
|
||||
|
||||
---Returns a setting and will try to convert to the given defaultvar type. Fallback to vDefaultVar if nil.
|
||||
---@param sName string
|
||||
---@param vDefaultVar? any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.Get(sName,vDefaultVar)
|
||||
local obj = settings[sName]
|
||||
if not obj then return vDefaultVar end
|
||||
return obj:GetValue()
|
||||
end
|
||||
|
||||
---Returns hte setting object.
|
||||
---@param sName string
|
||||
---@return SF2Convar
|
||||
---@shared
|
||||
function StormFox2.Setting.GetObject(sName)
|
||||
return settings[sName]
|
||||
end
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Sets a StormFox setting
|
||||
---------------------------------------------------------------------------]]
|
||||
local w_list = {
|
||||
"openweathermap_key", "openweathermap_real_lat", "openweathermap_real_lon"
|
||||
}
|
||||
--value_type["openweathermap_key"] = "string"
|
||||
--value_type["openweathermap_real_lat"] = "string"
|
||||
--value_type["openweathermap_real_lon"] = "string"
|
||||
|
||||
local function CallBack( sName, newVar, oldVar)
|
||||
if not settingCallback[sName] then return end
|
||||
for id, fFunc in pairs( settingCallback[sName] ) do
|
||||
if isstring(id) or IsValid(id) then
|
||||
fFunc(newVar, oldVar, sName, id)
|
||||
else -- Invalid
|
||||
settingCallback[sName][id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Tries to set a setting.
|
||||
---@param sName string
|
||||
---@param vVar any
|
||||
---@param bDontSave boolean
|
||||
---@return boolean saved
|
||||
function StormFox2.Setting.Set(sName,vVar, bDontSave)
|
||||
-- Check if valid
|
||||
local obj = settings[sName]
|
||||
if not obj then
|
||||
StormFox2.Warning("Invalid setting: " .. sName .. "!")
|
||||
return false
|
||||
end
|
||||
-- Check the variable
|
||||
if obj.type ~= type(vVar) then
|
||||
if type(vVar) == "string" then -- Try and convert it
|
||||
vVar = StringToValue( vVar, obj.type )
|
||||
else
|
||||
StormFox2.Warning("Invalid variable: " .. sName .. "!")
|
||||
return false
|
||||
end
|
||||
if vVar == nil then return false end -- Failed
|
||||
end
|
||||
-- Check min and max
|
||||
if obj.type == "number" and obj.min then
|
||||
vVar = math.max(vVar, obj.min)
|
||||
end
|
||||
if obj.type == "number" and obj.max then
|
||||
vVar = math.min(vVar, obj.max)
|
||||
end
|
||||
-- Check for duplicates
|
||||
local oldVar = obj:GetValue()
|
||||
if oldVar == vVar then return end -- Same value, ignore
|
||||
-- We need to ask the server to change this setting. This isn't ours
|
||||
if CLIENT and obj:IsServer() then
|
||||
if StormFox2.Permission then
|
||||
StormFox2.Permission.RequestSetting(sName, vVar)
|
||||
else
|
||||
StormFox2.Warning("Unable to ask server to change: " .. sName .. "!")
|
||||
end
|
||||
return false
|
||||
end
|
||||
-- Save the value
|
||||
-- Make callbacks
|
||||
local oB = blockSaveFile -- Editing a setting, might change others. Only save after we're done
|
||||
blockSaveFile = true
|
||||
local oldVar = obj.value
|
||||
obj.value = vVar
|
||||
-- Callback
|
||||
CallBack(sName, vVar, oldVar)
|
||||
blockSaveFile = oB -- We're done changing settings, save if we can
|
||||
if not blockSaveFile and not bDontSave then
|
||||
if not (sName == "mapfile" or sName == "mapfile_cl") then
|
||||
saveToFile()
|
||||
end
|
||||
end
|
||||
cache[sName] = nil -- Delete cache
|
||||
-- Tell all clients about it
|
||||
if SERVER then
|
||||
if not obj:IsSecret() then
|
||||
net.Start( StormFox2.Net.Settings )
|
||||
net.WriteUInt(NET_UPDATE, 3)
|
||||
net.WriteString(sName)
|
||||
net.WriteType(vVar)
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
--[[<Shared>------------------------------------------------------------------
|
||||
Gets called when a StormFox setting changes.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run("StormFox2.Setting.Change",sName,vVar, oldVar)
|
||||
return true
|
||||
end
|
||||
-- Server and Clientside NET
|
||||
if CLIENT then
|
||||
net.Receive(StormFox2.Net.Settings,function(len)
|
||||
local _type = net.ReadUInt(3)
|
||||
if _type == NET_UPDATE then
|
||||
local sName = net.ReadString()
|
||||
local var = net.ReadType()
|
||||
local obj = settings[sName]
|
||||
if not obj then
|
||||
StormFox2.Warning("Server tried to set an unknown setting: " .. sName)
|
||||
return
|
||||
end
|
||||
if not obj:IsServer() then
|
||||
StormFox2.Warning("Server tried to set a clientside setting: " .. sName)
|
||||
else
|
||||
local oldVar = obj.value
|
||||
obj.value = var
|
||||
cache[sName] = var
|
||||
-- Callback
|
||||
CallBack(sName, var, oldVar)
|
||||
hook.Run("StormFox2.Setting.Change",sName,obj.value,oldVar)
|
||||
end
|
||||
elseif _type == NET_ALLSETTINGS then
|
||||
local tab = net.ReadTable() -- I'm lazy
|
||||
for sName, vVar in pairs( tab ) do
|
||||
local obj = settings[sName]
|
||||
if not obj then -- So this setting "might" be used later. Cache the setting-value and ignore
|
||||
postSettingChace[sName] = vVar
|
||||
-- StormFox2.Warning("Server tried to set an unknown setting: " .. sName)
|
||||
continue
|
||||
end
|
||||
if not obj:IsServer() then -- This is a clientside setting. Nope.AVI
|
||||
StormFox2.Warning("Server tried to set a clientside setting: " .. sName)
|
||||
else
|
||||
local oldVar = obj.value
|
||||
obj.value = vVar
|
||||
cache[sName] = vVar
|
||||
-- Callback
|
||||
CallBack(sName, vVar, oldVar)
|
||||
hook.Run("StormFox2.Setting.Change",sName,obj.value,oldVar)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
hook.Add("StormFox2.data.initspawn", "StormFox2.setting.send", function( ply )
|
||||
net.Start( StormFox2.Net.Settings )
|
||||
net.WriteUInt(NET_ALLSETTINGS, 3)
|
||||
for sName, obj in pairs( settings ) do
|
||||
if not obj then continue end
|
||||
if obj:IsSecret() then continue end
|
||||
net.WriteType( sName )
|
||||
net.WriteType( obj:GetValue() )
|
||||
end
|
||||
-- End of table
|
||||
net.WriteType( nil )
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
---Calls the function when the given setting changes.
|
||||
---fFunc will be called with: vNewVariable, vOldVariable, ConVarName, sID.
|
||||
---Unlike convars, server-setings will also be triggered on the clients too.
|
||||
---@param sName string
|
||||
---@param fFunc function
|
||||
---@param sID any
|
||||
---@shared
|
||||
function StormFox2.Setting.Callback(sName,fFunc,sID)
|
||||
if not settingCallback[sName] then settingCallback[sName] = {} end
|
||||
settingCallback[sName][sID or "default"] = fFunc
|
||||
end
|
||||
|
||||
--hook.Add("StormFox2.Setting.Change", "StormFox2.Setting.Callbacks", function(sName, vVar, oldVar)
|
||||
--if not settingCallback[sName] then return end
|
||||
--for id, fFunc in pairs( settingCallback[sName] ) do
|
||||
-- fFunc(vVar, oldVar, sName, id)
|
||||
--end
|
||||
--end)
|
||||
|
||||
---Same as StormFox2.Setting.Get, however this will cache the result.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCache(sName,vDefaultVar)
|
||||
if cache[sName] then return cache[sName] end
|
||||
local var = StormFox2.Setting.Get(sName,vDefaultVar)
|
||||
cache[sName] = var
|
||||
return var
|
||||
end
|
||||
|
||||
---Returns the default setting
|
||||
---@param sName string
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.GetDefault(sName)
|
||||
local obj = settings[sName]
|
||||
if not obj then return nil end
|
||||
return obj:GetDefault()
|
||||
end
|
||||
|
||||
---Returns all known settings. Clients will reitrhn both server and client settings.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Setting.GetAll()
|
||||
return table.GetKeys( settings )
|
||||
end
|
||||
|
||||
---Returns all server settings.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Setting.GetAllServer()
|
||||
-- Server only has server settings
|
||||
if SERVER then return StormFox2.Setting.GetAll() end
|
||||
-- Make list
|
||||
local t = {}
|
||||
for sName,obj in pairs(settings) do
|
||||
if not obj:IsServer() then continue end
|
||||
table.insert(t, sName)
|
||||
end
|
||||
return t
|
||||
end
|
||||
if CLIENT then
|
||||
---Returns all client settings.
|
||||
---@return table
|
||||
function StormFox2.Setting.GetAllClient()
|
||||
local t = {}
|
||||
for sName,obj in pairs(settings) do
|
||||
if obj:IsServer() then continue end
|
||||
table.insert(t, sName)
|
||||
end
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the valuetype of the setting. This can allow special types like tables .. ect
|
||||
local type_override = {}
|
||||
|
||||
---Returns the settigns variable type.
|
||||
---@param sName string
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetType( sName )
|
||||
if type_override[sName] then return type_override[sName] end
|
||||
local obj = settings[sName]
|
||||
if not obj then return end
|
||||
return obj.type
|
||||
end
|
||||
--[[ Type:
|
||||
- number
|
||||
- string
|
||||
- float
|
||||
- boolean
|
||||
- A table of options { [value] = "description" }
|
||||
- special_float
|
||||
Marks below 0 as "off"
|
||||
- time
|
||||
- temp / temperature
|
||||
- Time_toggle
|
||||
]]
|
||||
|
||||
---Sets setting's variable type. Can also use special types like "time".
|
||||
---@param sName string
|
||||
---@param sType string
|
||||
---@param tSortOrter table
|
||||
---@shared
|
||||
function StormFox2.Setting.SetType( sName, sType, tSortOrter )
|
||||
if type(sType) == "nil" then -- Reset it
|
||||
StormFox2.Warning("Can't make the setting a nil-type!")
|
||||
end
|
||||
if type(sType) == "boolean" then
|
||||
type_override[sName] = "boolean"
|
||||
elseif type(sType) == "number" then
|
||||
type_override[sName] = "number"
|
||||
elseif type(sType) == "table" then -- A table is a list of options
|
||||
type_override[sName] = {sType, tSortOrter}
|
||||
else
|
||||
if sType == "bool" then
|
||||
type_override[sName] = "boolean"
|
||||
else
|
||||
type_override[sName] = string.lower(sType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets all stormfox settings to default.
|
||||
if SERVER then
|
||||
local obj = StormFox2.Setting.AddSV("mapfile", false)
|
||||
obj:AddCallback(StormFox2.Setting.UseMapFile)
|
||||
obj:SetValue( StormFox2.Setting.IsUsingMapFile() )
|
||||
---Returns all settings back to default.
|
||||
---@server
|
||||
function StormFox2.Setting.Reset()
|
||||
blockSaveFile = true
|
||||
for sName, obj in pairs(settings) do
|
||||
if sName == "mapfile" then continue end
|
||||
obj:Revert()
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were reset to default values. You should restart!")
|
||||
cache = {}
|
||||
end
|
||||
else
|
||||
StormFox2.Setting.AddSV("mapfile", false)
|
||||
local obj = StormFox2.Setting.AddCL("mapfile_cl", false)
|
||||
obj:AddCallback(StormFox2.Setting.UseMapFile)
|
||||
obj:SetValue( StormFox2.Setting.IsUsingMapFile() )
|
||||
---Returns all settings back to default.
|
||||
---@client
|
||||
function StormFox2.Setting.Reset()
|
||||
blockSaveFile = true
|
||||
for _, sName in ipairs(StormFox2.Setting.GetAllClient()) do
|
||||
if sName == "mapfile_cl" then continue end
|
||||
local obj = setting[sName]
|
||||
if not obj then continue end
|
||||
obj:Revert()
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were reset to default values. You should rejoin!")
|
||||
cache = {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets and sets StormFox server setting
|
||||
if SERVER then
|
||||
---Parses a CVS string and applies all settings to SF2.
|
||||
---@param str string
|
||||
---@server
|
||||
function StormFox2.Setting.SetCVS( str )
|
||||
local t = string.Explode(",", str)
|
||||
blockSaveFile = true
|
||||
for i = 1, #t, 2 do
|
||||
local sName, var = t[i], t[i+1] or nil
|
||||
if string.len(sName) < 1 or not var then continue end
|
||||
local obj = StormFox2.Setting.GetObject(sName )
|
||||
if not obj then
|
||||
StormFox2.Warning("Invalid setting: " .. sName .. ".")
|
||||
continue
|
||||
else
|
||||
obj:SetValue(var)
|
||||
end
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were updated. You should restart!")
|
||||
end
|
||||
end
|
||||
|
||||
local exlist = {"openweathermap_real_lat", "openweathermap_real_lon", "openweathermap_key"}
|
||||
---Compiles all server-settigns into a CVS string.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCVS()
|
||||
local c = ""
|
||||
for sName, obj in pairs(settings) do
|
||||
if obj:IsSecret() then continue end
|
||||
if not obj:IsServer() then continue end
|
||||
c = c .. sName .. "," .. obj:GetString() .. ","
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
---Compiles all default server-settings into a CVS string.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCVSDefault()
|
||||
local c = ""
|
||||
for sName, obj in pairs(settings) do
|
||||
if obj:IsSecret() then continue end -- Justi n case, so people don't share hidden settings
|
||||
if not obj:IsServer() then continue end
|
||||
c = c .. sName .. "," .. ValueToString(obj:GetDefault(), obj:GetType()) .. ","
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
-- Disable SF2
|
||||
StormFox2.Setting.AddSV("enable", true, nil, "Start")
|
||||
StormFox2.Setting.AddSV("allow_csenable", engine.ActiveGamemode() == "sandbox", nil, "Start")
|
||||
if CLIENT then
|
||||
StormFox2.Setting.AddCL("clenable", true, nil, "Start")
|
||||
end
|
||||
|
||||
---Returns true if SF2 is enabled.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.SFEnabled()
|
||||
if not StormFox2.Setting.GetCache("enable", true) then return false end
|
||||
if SERVER or not StormFox2.Setting.GetCache("allow_csenable", false) then return true end
|
||||
return StormFox2.Setting.GetCache("clenable", true)
|
||||
end
|
||||
371
lua/stormfox2/lib/sh_terrain.lua
Normal file
371
lua/stormfox2/lib/sh_terrain.lua
Normal file
@@ -0,0 +1,371 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Terrain control. Changes the ground
|
||||
|
||||
Terrain.Create( sName ) Creates a new terrain type and stores it
|
||||
Terrain.Get( sName ) Returns the terrain.
|
||||
Terrain.Set( sName ) Sets the terrain. (This should only be done serverside)
|
||||
Terrain.GetCurrent() Returns the terrain obj or nil.
|
||||
StormFox2.Terrain.Reset() Resets the terrain to default.
|
||||
StormFox2.Terrain.HasMaterialChanged( iMaterial ) Returns true if the terrain has changed the material.
|
||||
|
||||
Terrain Meta:
|
||||
:LockUntil( fFunc ) Makes the terrian
|
||||
:MakeFootprints( sndList, sndName ) Makes footprints. Allows to overwrite footstep sounds.
|
||||
:AddTextureSwap( material, basetexture, basetextire2 ) Changes a materials textures.
|
||||
:RenderWindow( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindowRefract( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindow64x64( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindowRefract64x64( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:Apply Applies the terrain (This won't reset old terrain)
|
||||
|
||||
Hooks:
|
||||
StormFox2.terrain.footstep Entity foot[0 = left,1 = right] sTexture bTerrainTexture
|
||||
]]
|
||||
|
||||
---@class SF2Terrain
|
||||
local meta = {}
|
||||
meta.__index = meta
|
||||
meta.__tostring = function(self) return "SF_TerrainType[" .. (self.Name or "Unknwon") .. "]" end
|
||||
meta.__eq = function(self, other)
|
||||
if type(other) ~= "table" then return false end
|
||||
if not other.Name then return false end
|
||||
return other.Name == self.Name
|
||||
end
|
||||
debug.getregistry()["SFTerrain"] = meta
|
||||
local terrains = {}
|
||||
StormFox2.Terrain = {}
|
||||
|
||||
--- Creates a new terrain type, stores and returns it.
|
||||
---@param sName string
|
||||
---@return SF2Terrain
|
||||
---@shared
|
||||
function StormFox2.Terrain.Create( sName )
|
||||
local t = {}
|
||||
t.Name = sName
|
||||
setmetatable(t, meta)
|
||||
terrains[sName] = t
|
||||
t.swap = {}
|
||||
return t
|
||||
end
|
||||
|
||||
local CURRENT_TERRAIN
|
||||
---Returns the current applies terrain.
|
||||
---@return SF2Terrain|nil
|
||||
---@shared
|
||||
function StormFox2.Terrain.GetCurrent()
|
||||
return CURRENT_TERRAIN
|
||||
end
|
||||
|
||||
---Returns a terrain by name.
|
||||
---@param sName string
|
||||
---@return SF2Terrain|nil
|
||||
---@shared
|
||||
function StormFox2.Terrain.Get( sName )
|
||||
if not sName then return end
|
||||
return terrains[sName]
|
||||
end
|
||||
|
||||
-- Makes the terrain stay until this function returns true or another terrain overwrites.
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:LockUntil( fFunc )
|
||||
self.lock = fFunc
|
||||
end
|
||||
|
||||
---Sets the ground texture. e.i; snow texture.
|
||||
---@param iTexture string
|
||||
---@param bOnlyGround boolean
|
||||
---@shared
|
||||
function meta:SetGroundTexture( iTexture, bOnlyGround )
|
||||
self.ground = iTexture
|
||||
self.only_ground = bOnlyGround
|
||||
end
|
||||
|
||||
-- Adds a texture swap for when this terrain gets applied.
|
||||
---@param mMaterial Material|string
|
||||
---@param basetexture string
|
||||
---@param basetextire2 string
|
||||
---@shared
|
||||
function meta:AddTextureSwap( mMaterial, basetexture, basetextire2 )
|
||||
if type(mMaterial) ~= "IMaterial" then
|
||||
mMaterial = Material(mMaterial)
|
||||
end
|
||||
if not basetexture and not basetextire2 then return end
|
||||
self.swap[mMaterial] = { basetexture, basetextire2 }
|
||||
end
|
||||
|
||||
---Makes footprints and allows to overwrite default footstep sounds.
|
||||
---@param bool boolean
|
||||
---@param sndList? table
|
||||
---@param sndName? string
|
||||
---@param OnPrint? function
|
||||
---@shared
|
||||
function meta:MakeFootprints( bool, sndList, sndName, OnPrint )
|
||||
self.footprints = bool
|
||||
if sndList or sndName then
|
||||
self.footprintSnds = {sndList, sndName}
|
||||
end
|
||||
self.footstepFunc = OnPrint
|
||||
self.footstepLisen = bool or sndList or sndName or OnPrint
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindow( fFunc )
|
||||
self.windRender = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindowRefract( fFunc )
|
||||
self.windRenderRef = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindow64x64( fFunc )
|
||||
self.windRender64 = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindowRefract64x64( fFunc )
|
||||
self.windRenderRef64 = fFunc
|
||||
end
|
||||
|
||||
-- Texture handler
|
||||
_STORMFOX_TEXCHANGES = _STORMFOX_TEXCHANGES or {} -- List of changed materials.
|
||||
_STORMFOX_TEXORIGINAL = _STORMFOX_TEXORIGINAL or {} -- This is global, just in case.
|
||||
local footStepLisen = false -- If set to true, will enable footprints.
|
||||
|
||||
local function StringTex(iTex)
|
||||
if not iTex then return end
|
||||
if type(iTex) == "string" then return iTex end
|
||||
return iTex:GetName()
|
||||
end
|
||||
|
||||
local function HasChanged( self, materialTexture )
|
||||
local mat = self:GetName() or "unknown"
|
||||
local b = materialTexture == "$basetexture2" and 2 or 1
|
||||
return _STORMFOX_TEXCHANGES[mat] and _STORMFOX_TEXCHANGES[mat][b] or false
|
||||
end
|
||||
|
||||
---Returns true if the material has changed.
|
||||
---@param iMaterial Material
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.HasMaterialChanged( iMaterial )
|
||||
local mat = iMaterial:GetName() or iMaterial
|
||||
return _STORMFOX_TEXCHANGES[mat] and true or false
|
||||
end
|
||||
|
||||
---Returns the original texture for said material.
|
||||
---@param iMaterial Material
|
||||
---@return stirng
|
||||
---@shared
|
||||
function StormFox2.Terrain.GetOriginalTexture( iMaterial )
|
||||
local mat = iMaterial:GetName() or iMaterial
|
||||
return _STORMFOX_TEXORIGINAL[mat] and _STORMFOX_TEXORIGINAL[mat][1]
|
||||
end
|
||||
|
||||
-- We're going to overwrite SetTexture. As some mods might change the default texture.
|
||||
local mat_meta = FindMetaTable("IMaterial")
|
||||
STORMFOX_TEX_APPLY = STORMFOX_TEX_APPLY or mat_meta.SetTexture
|
||||
function mat_meta:SetTexture(materialTexture, texture)
|
||||
-- Check if it is basetexutre or basetexture2 we're changing.
|
||||
if materialTexture ~= "$basetexture" and materialTexture ~= "$basetexture2" then
|
||||
return STORMFOX_TEX_APPLY( self, materialTexture, texture )
|
||||
end
|
||||
-- Overwrite the original texture list.
|
||||
local mat = self:GetName() or "unknown"
|
||||
if not _STORMFOX_TEXORIGINAL[mat] then _STORMFOX_TEXORIGINAL[mat] = {} end
|
||||
if materialTexture == "$basetexture" then
|
||||
_STORMFOX_TEXORIGINAL[mat][1] = StringTex(texture)
|
||||
else
|
||||
_STORMFOX_TEXORIGINAL[mat][2] = StringTex(texture)
|
||||
end
|
||||
-- If we havn't changed the texture, allow change.
|
||||
if not HasChanged(self, materialTexture) then
|
||||
return STORMFOX_TEX_APPLY( self, materialTexture, texture )
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets the material. Returns false if unable to reset.
|
||||
local function ResetMaterial( self )
|
||||
local mat = self:GetName() or "unknown"
|
||||
if not _STORMFOX_TEXCHANGES[mat] or not _STORMFOX_TEXORIGINAL[mat] then return false end
|
||||
if _STORMFOX_TEXCHANGES[mat][1] and _STORMFOX_TEXORIGINAL[mat][1] then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture", _STORMFOX_TEXORIGINAL[mat][1] )
|
||||
end
|
||||
if _STORMFOX_TEXCHANGES[mat][2] and _STORMFOX_TEXORIGINAL[mat][2] then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture2", _STORMFOX_TEXORIGINAL[mat][2] )
|
||||
end
|
||||
_STORMFOX_TEXCHANGES[mat] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
-- Set the material
|
||||
local function SetMat(self, tex1, tex2)
|
||||
if not tex1 and not tex2 then return end
|
||||
local mat = self:GetName() or "unknown"
|
||||
-- Save the default texture
|
||||
if not _STORMFOX_TEXORIGINAL[mat] then _STORMFOX_TEXORIGINAL[mat] = {} end
|
||||
if tex1 and not _STORMFOX_TEXORIGINAL[mat][1] then
|
||||
_STORMFOX_TEXORIGINAL[mat][1] = StringTex(self:GetTexture("$basetexture"))
|
||||
end
|
||||
if tex2 and not _STORMFOX_TEXORIGINAL[mat][2] then
|
||||
_STORMFOX_TEXORIGINAL[mat][2] = StringTex(self:GetTexture("$basetexture2"))
|
||||
end
|
||||
-- Set texture
|
||||
if tex1 then
|
||||
if CLIENT then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture", tex1 )
|
||||
end
|
||||
if not _STORMFOX_TEXCHANGES[ mat ] then _STORMFOX_TEXCHANGES[ mat ] = {} end
|
||||
_STORMFOX_TEXCHANGES[ mat ][ 1 ] = true
|
||||
end
|
||||
if tex2 then
|
||||
if CLIENT then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture2", tex2 )
|
||||
end
|
||||
if not _STORMFOX_TEXCHANGES[ mat ] then _STORMFOX_TEXCHANGES[ mat ] = {} end
|
||||
_STORMFOX_TEXCHANGES[ mat ][ 2 ] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets the terrain to default. Setting bNoUpdate to true on the server, will not notify the clients or relays.
|
||||
---@param bNoUpdate boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.Reset( bNoUpdate )
|
||||
--print("Reset")
|
||||
if SERVER and not bNoUpdate then
|
||||
StormFox2.Map.CallLogicRelay("terrain_clear")
|
||||
end
|
||||
CURRENT_TERRAIN = nil
|
||||
if SERVER and not bNoUpdate then
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( "" )
|
||||
net.Broadcast()
|
||||
end
|
||||
if next(_STORMFOX_TEXCHANGES) == nil then return end
|
||||
for tex,_ in pairs( _STORMFOX_TEXCHANGES ) do
|
||||
local mat = Material( tex )
|
||||
if not ResetMaterial( mat ) then
|
||||
StormFox2.Warning( "Can't reset [" .. tostring( mat ) .. "]." )
|
||||
end
|
||||
end
|
||||
_STORMFOX_TEXCHANGES = {}
|
||||
end
|
||||
|
||||
--- Sets the terrain. (This should only be done serverside)
|
||||
---@param sName string
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.Set( sName )
|
||||
-- Apply terrain.
|
||||
local t = StormFox2.Terrain.Get( sName )
|
||||
if not t then
|
||||
StormFox2.Terrain.Reset()
|
||||
return false
|
||||
end
|
||||
StormFox2.Terrain.Reset( true )
|
||||
t:Apply()
|
||||
if SERVER then
|
||||
StormFox2.Map.CallLogicRelay( "terrain_" .. string.lower(sName) )
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Reapplies the current terrain
|
||||
---@shared
|
||||
function StormFox2.Terrain.Update()
|
||||
local terrain = StormFox2.Terrain.GetCurrent()
|
||||
if not terrain then return end
|
||||
StormFox2.Terrain.Reset( true )
|
||||
terrain:Apply()
|
||||
end
|
||||
|
||||
local NO_TYPE = -1
|
||||
local DIRTGRASS_TYPE = 0
|
||||
local ROOF_TYPE = 1
|
||||
local ROAD_TYPE = 2
|
||||
local PAVEMENT_TYPE = 3
|
||||
|
||||
local function checkType(n, bOnlkyG)
|
||||
if not n then return false end
|
||||
if n == 0 then return true end
|
||||
if n == 1 and not bOnlkyG then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Applies the terrain (This won't reset old terrain)
|
||||
function meta:Apply()
|
||||
CURRENT_TERRAIN = self
|
||||
if SERVER then
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( CURRENT_TERRAIN.Name )
|
||||
net.Broadcast()
|
||||
end
|
||||
-- Swap materials
|
||||
if self.swap then
|
||||
for mat,tab in pairs( self.swap ) do
|
||||
SetMat( mat, tab[1], tab[2] )
|
||||
end
|
||||
end
|
||||
-- Set ground
|
||||
if self.ground then
|
||||
for materialName,tab in pairs( StormFox2.Map.GetTextureTree() ) do
|
||||
local mat = Material( materialName )
|
||||
local a,b = checkType(tab[1], self.only_ground), checkType(tab[2], self.only_ground)
|
||||
SetMat( mat, a and self.ground,b and self.ground)
|
||||
end
|
||||
end
|
||||
footStepLisen = self.footprints or self.footprintSnds
|
||||
end
|
||||
|
||||
-- NET
|
||||
if SERVER then
|
||||
net.Receive(StormFox2.Net.Terrain, function(len, ply) -- OI, what terrain?
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( CURRENT_TERRAIN and CURRENT_TERRAIN.Name or "" )
|
||||
net.Send(ply)
|
||||
end)
|
||||
else
|
||||
net.Receive(StormFox2.Net.Terrain, function(len)
|
||||
local sName = net.ReadString()
|
||||
local b = StormFox2.Terrain.Set( sName )
|
||||
--"Terrain Recived: ", sName, b)
|
||||
|
||||
end)
|
||||
-- Ask the server
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.terrain.init", function()
|
||||
timer.Simple(1, function()
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteBit(1)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
-- A bit ballsy, but lets try.
|
||||
hook.Add("ShutDown","StormFox2.Terrain.Clear",function()
|
||||
StormFox2.Msg("Reloading all changed materials ..")
|
||||
for k, _ in pairs( _STORMFOX_TEXORIGINAL ) do
|
||||
RunConsoleCommand("mat_reloadmaterial", k)
|
||||
end
|
||||
end)
|
||||
256
lua/stormfox2/lib/sh_util.lua
Normal file
256
lua/stormfox2/lib/sh_util.lua
Normal file
@@ -0,0 +1,256 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Useful functions
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
StormFox2.util = {}
|
||||
local cache = {}
|
||||
|
||||
---Returns the OBBMins and OBBMaxs of a model.
|
||||
---@param sModel string
|
||||
---@return Vector MinSize
|
||||
---@return Vector MaxSize
|
||||
---@shared
|
||||
function StormFox2.util.GetModelSize(sModel)
|
||||
if cache[sModel] then return cache[sModel][1],cache[sModel][2] end
|
||||
if not file.Exists(sModel,"GAME") then
|
||||
cache[sModel] = {Vector(0,0,0),Vector(0,0,0)}
|
||||
return cache[sModel]
|
||||
end
|
||||
local f = file.Open(sModel,"r", "GAME")
|
||||
f:Seek(104)
|
||||
local hullMin = Vector( f:ReadFloat(),f:ReadFloat(),f:ReadFloat())
|
||||
local hullMax = Vector( f:ReadFloat(),f:ReadFloat(),f:ReadFloat())
|
||||
f:Close()
|
||||
cache[sModel] = {hullMin,hullMax}
|
||||
return hullMin,hullMax
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
--[[-----------------------------------------------------------------
|
||||
Calcview results
|
||||
---------------------------------------------------------------------------]]
|
||||
local view = {}
|
||||
view.pos = Vector(0,0,0)
|
||||
view.ang = Angle(0,0,0)
|
||||
view.fov = 0
|
||||
view.drawviewer = false
|
||||
local otherPos, otherAng, otherFOV
|
||||
local a = true
|
||||
hook.Add("RenderScene", "StormFox2.util.EyeHack", function(pos, ang,fov)
|
||||
if not a then return end
|
||||
otherPos, otherAng, otherFOV = pos, ang,fov
|
||||
a = false
|
||||
end)
|
||||
|
||||
hook.Add("PostRender", "StormFox2.util.EyeHack", function()
|
||||
local tab = render.GetViewSetup and render.GetViewSetup() or {}
|
||||
view.pos = tab.origin or otherPos or EyePos()
|
||||
view.ang = tab.angles or otherAng or EyeAngles()
|
||||
view.fov = tab.fov or otherFOV or 90
|
||||
view.drawviewer = LocalPlayer():ShouldDrawLocalPlayer()
|
||||
a = true
|
||||
end)
|
||||
|
||||
---Returns the last calcview result.
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.util.GetCalcView()
|
||||
return view
|
||||
end
|
||||
|
||||
---Returns the last camera position.
|
||||
---@return Vector
|
||||
---@client
|
||||
function StormFox2.util.RenderPos()
|
||||
return view.pos or EyePos()
|
||||
end
|
||||
|
||||
---Returns the last camera angle.
|
||||
---@return Angle
|
||||
---@client
|
||||
function StormFox2.util.RenderAngles()
|
||||
return view.ang or RenderAngles()
|
||||
end
|
||||
|
||||
--[[<Client>-----------------------------------------------------------------
|
||||
Returns the current viewentity
|
||||
---------------------------------------------------------------------------]]
|
||||
local viewEntity
|
||||
hook.Add("Think", "StormFox2.util.ViewEnt", function()
|
||||
local lp = LocalPlayer()
|
||||
if not IsValid(lp) then return end
|
||||
local p = lp:GetViewEntity() or lp
|
||||
if p.InVehicle and p:InVehicle() and p == lp then
|
||||
viewEntity = p:GetVehicle() or p
|
||||
else
|
||||
viewEntity = p
|
||||
end
|
||||
end)
|
||||
---Returns the current viewentity.
|
||||
---@return Entity
|
||||
---@client
|
||||
function StormFox2.util.ViewEntity()
|
||||
return IsValid(viewEntity) and viewEntity or LocalPlayer()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Color interpolation suck.
|
||||
Mixing an orange and blue color can result in a greenish one.
|
||||
This is not how sky colors work, so we make our own CCT object here that can be mixed instead.
|
||||
]]
|
||||
|
||||
local log,Clamp,pow = math.log, math.Clamp, math.pow
|
||||
---@class SF2CCT_Color
|
||||
local meta = {}
|
||||
function meta.__index( a, b )
|
||||
return meta[b] or a._col[b]
|
||||
end
|
||||
meta.__MetaName = "CCT_Color"
|
||||
local function CCTToRGB( nKelvin )
|
||||
kelvin = math.Clamp(nKelvin, 1000, 40000)
|
||||
local tmp = kelvin / 100
|
||||
local r, g, b = 0,0,0
|
||||
if tmp <= 66 then
|
||||
r = 255
|
||||
g = 99.4708025861 * log(tmp) - 161.1195681661
|
||||
else
|
||||
r = 329.698727446 * pow(tmp - 60, -0.1332047592)
|
||||
g = 288.1221695283 * pow(tmp - 60, -0.0755148492)
|
||||
end
|
||||
if tmp >= 66 then
|
||||
b = 255
|
||||
elseif tmp <= 19 then
|
||||
b = 0
|
||||
else
|
||||
b = 138.5177312231 * log(tmp - 10) - 305.0447927307
|
||||
end
|
||||
if nKelvin < 1000 then
|
||||
local f = (nKelvin / 1000)
|
||||
r = r * f
|
||||
g = g * f
|
||||
b = b * f
|
||||
end
|
||||
return Color(Clamp(r, 0, 255), Clamp(g, 0, 255), Clamp(b, 0, 255))
|
||||
end
|
||||
|
||||
---Returns a CCT Color object.
|
||||
---@param kelvin number
|
||||
---@return SF2CCT_Color
|
||||
function StormFox2.util.CCTColor( kelvin )
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t._kelvin = kelvin
|
||||
t._col = CCTToRGB( kelvin )
|
||||
return t
|
||||
end
|
||||
|
||||
function meta:ToRGB()
|
||||
return self._col
|
||||
end
|
||||
|
||||
function meta:SetKelvin( kelvin )
|
||||
self._kelvin = kelvin
|
||||
self._col = CCTToRGB( kelvin )
|
||||
return self
|
||||
end
|
||||
|
||||
function meta:GetKelvin()
|
||||
return self._kelvin
|
||||
end
|
||||
|
||||
function meta.__add( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = b:GetKelvin() + a
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() + b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t + b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta.__sub( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a - b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() - b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t - b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta.__mul( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a * b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() * b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t * b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta:__div( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a / b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() / b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t / b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
---Renders a range of colors in the console.
|
||||
---@param from number
|
||||
---@param to number
|
||||
---@param len number
|
||||
function StormFox2.util.CCTColorDebug( from, to, len )
|
||||
len = len or 60
|
||||
from = from or 2200
|
||||
to = to or 12000
|
||||
local a = (to - from) / len
|
||||
Msg(from .. " [")
|
||||
for i = 1, len do
|
||||
MsgC(StormFox2.util.CCTColor(from + a * i) , "▉" )
|
||||
end
|
||||
Msg("] " .. to)
|
||||
MsgN()
|
||||
end
|
||||
44
lua/stormfox2/lib/sh_version.lua
Normal file
44
lua/stormfox2/lib/sh_version.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- Checks the newest version
|
||||
if SERVER then
|
||||
-- Checks the workshop page for version number.
|
||||
local function RunCheck()
|
||||
http.Fetch(StormFox2.WorkShopURL, function(code)
|
||||
local lV = tonumber(string.match(code, "Version:(.-)<"))
|
||||
if not lV then return end -- Unable to locate last version
|
||||
if StormFox2.Version >= lV then return end -- Up to date
|
||||
StormFox2.Msg("Version " .. lV .. " is out!")
|
||||
StormFox2.Network.Set("stormfox_newv", lV)
|
||||
cookie.Set("sf_nextv", lV)
|
||||
end)
|
||||
end
|
||||
local function RunLogic()
|
||||
-- Check if a newer version is out
|
||||
local lV = cookie.GetNumber("sf_nextv", StormFox2.Version)
|
||||
if cookie.GetNumber("sf_nextvcheck", 0) > os.time() then
|
||||
if lV > StormFox2.Version then
|
||||
StormFox2.Msg("Version " .. lV .. " is out!")
|
||||
StormFox2.Network.Set("stormfox_newv", lV)
|
||||
end
|
||||
else
|
||||
RunCheck()
|
||||
cookie.Set("sf_nextvcheck", os.time() + 129600) -- Check in 1½ day
|
||||
end
|
||||
end
|
||||
hook.Add("stormfox2.preinit", "stormfox2.checkversion", RunLogic)
|
||||
end
|
||||
|
||||
-- Will return a version-number, if a new version is detected
|
||||
---@return number
|
||||
function StormFox2.NewVersion()
|
||||
return StormFox2.Data.Get("stormfox_newv")
|
||||
end
|
||||
287
lua/stormfox2/lib/sh_weather_meta.lua
Normal file
287
lua/stormfox2/lib/sh_weather_meta.lua
Normal file
@@ -0,0 +1,287 @@
|
||||
--[[
|
||||
| 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.Weather = {}
|
||||
local Weathers = {}
|
||||
-- Diffrent stamps on where the sun are. (Remember, SF2 goes after sunrise/set)
|
||||
---@class SF_SKY_STAMP : number
|
||||
SF_SKY_DAY = 0
|
||||
SF_SKY_SUNRISE = 1
|
||||
SF_SKY_SUNSET = 2
|
||||
SF_SKY_CEVIL = 3
|
||||
SF_SKY_BLUE_HOUR = 4
|
||||
SF_SKY_NAUTICAL = 5
|
||||
SF_SKY_ASTRONOMICAL = 6
|
||||
SF_SKY_NIGHT = 7
|
||||
|
||||
---@class SF2_WeatherType
|
||||
local w_meta = {}
|
||||
w_meta.__index = w_meta
|
||||
w_meta.__tostring = function(self) return "SF_WeatherType[" .. (self.Name or "Unknwon") .. "]" end
|
||||
w_meta.MetaName = "SF-Weather"
|
||||
debug.getregistry()["SFWeather"] = w_meta
|
||||
|
||||
-- function for the generator. Returns true to allow. Function will be called with (day_temperature, time_start, time_duration, percent)
|
||||
---@deprecated
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:SetRequire(fFunc)
|
||||
self.Require = fFunc
|
||||
end
|
||||
|
||||
---Runs said function when the weather is applied.
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:SetInit(fFunc)
|
||||
self.Init = fFunc
|
||||
end
|
||||
|
||||
---Runs said function if the weather-amount change.
|
||||
---@param fFunc function
|
||||
---@deprecated
|
||||
---@shared
|
||||
function w_meta:SetOnChange(fFunc)
|
||||
self.OnChange = fFunc
|
||||
end
|
||||
|
||||
---Will always return true
|
||||
---@return boolean
|
||||
---@shared
|
||||
function w_meta:IsValid()
|
||||
return true
|
||||
end
|
||||
|
||||
---Creates and returns a new weather-type. Will not duplicate weather-types and instead return the one created before.
|
||||
---@param sName string
|
||||
---@param sInherit? string
|
||||
---@return SF2_WeatherType
|
||||
---@shared
|
||||
function StormFox2.Weather.Add( sName, sInherit )
|
||||
if Weathers[sName] then return Weathers[sName] end
|
||||
local t = {}
|
||||
t.ID = table.Count(Weathers) + 1
|
||||
t.Name = sName
|
||||
setmetatable(t, w_meta)
|
||||
if sName ~= "Clear" then -- Clear shouldn't inherit itself
|
||||
t.Inherit = sInherit or "Clear"
|
||||
end
|
||||
Weathers[sName] = t
|
||||
t.Function = {}
|
||||
t.Static = {}
|
||||
t.Dynamic = {}
|
||||
t.SunStamp = {}
|
||||
return t
|
||||
end
|
||||
|
||||
---Returns a weather-type by name.
|
||||
---@param sName string
|
||||
---@return SF2_WeatherType
|
||||
---@shared
|
||||
function StormFox2.Weather.Get( sName )
|
||||
return Weathers[sName]
|
||||
end
|
||||
|
||||
---Returns a list of all weather-types name.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetAll()
|
||||
return table.GetKeys( Weathers )
|
||||
end
|
||||
|
||||
---Returns all weathers that can be spawned.
|
||||
---@deprecated
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetAllSpawnable()
|
||||
local t = {}
|
||||
for w, v in pairs( Weathers ) do
|
||||
if v.spawnable and w ~= "Clear" then -- clear is default
|
||||
table.insert(t, w)
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local keys = {}
|
||||
local l_e,l_c, c_c = -1,0
|
||||
|
||||
---Sets a key-value.
|
||||
---@param sKey string
|
||||
---@param zVariable any|function
|
||||
---@param bStatic boolean
|
||||
---@shared
|
||||
function w_meta:Set(sKey,zVariable, bStatic)
|
||||
keys[sKey] = true
|
||||
l_c = CurTime()
|
||||
if type(zVariable) == "function" then
|
||||
self.Function[sKey] = zVariable
|
||||
elseif bStatic then
|
||||
self.Static[sKey] = zVariable
|
||||
else
|
||||
self.Dynamic[sKey] = zVariable
|
||||
end
|
||||
end
|
||||
|
||||
local r_list = {"Terrain", "windRender", "windRenderRef", "windRender64", "windRenderRef64"}
|
||||
---Returns all keys a weather has.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetKeys()
|
||||
if l_c == l_e then
|
||||
return c_c
|
||||
end
|
||||
for k,v in ipairs(r_list) do
|
||||
keys[v] = nil
|
||||
end
|
||||
l_e = l_c
|
||||
c_c = table.GetKeys(keys)
|
||||
return c_c
|
||||
end
|
||||
|
||||
--- This function inserts a variable into a table. Using the STAMP as key.
|
||||
---@param sKey string
|
||||
---@param zVariable any
|
||||
---@param stamp SF_SKY_STAMP
|
||||
---@shared
|
||||
function w_meta:SetSunStamp(sKey, zVariable, stamp)
|
||||
keys[sKey] = true
|
||||
l_c = CurTime()
|
||||
if not self.SunStamp[sKey] then self.SunStamp[sKey] = {} end
|
||||
self.SunStamp[sKey][stamp] = zVariable
|
||||
end
|
||||
|
||||
--- Returns a copy of all variables with the given sunstamp, to another given sunstamp.
|
||||
---@param from_STAMP SF_SKY_STAMP
|
||||
---@param to_STAMP SF_SKY_STAMP
|
||||
---@shared
|
||||
function w_meta:CopySunStamp( from_STAMP, to_STAMP )
|
||||
for sKey,v in pairs(self.SunStamp) do
|
||||
if type(v) ~= "table" then continue end
|
||||
if not v[from_STAMP] then continue end
|
||||
self.SunStamp[sKey][to_STAMP] = v[from_STAMP] or nil
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local in_list = {}
|
||||
|
||||
---Returns a variable. If the variable is a function, it will be called with the current stamp.
|
||||
---Second argument will tell SF it is static and shouldn't be mixed
|
||||
---@param sKey string
|
||||
---@param SUNSTAMP SF_SKY_STAMP
|
||||
---@return any
|
||||
---@return boolean isStatic
|
||||
---@shared
|
||||
function w_meta:Get(sKey, SUNSTAMP )
|
||||
-- Fallback to day-stamp, if Last Steamp is nil-
|
||||
if not SUNSTAMP then
|
||||
SUNSTAMP = StormFox2.Sky.GetLastStamp() or SF_SKY_DAY
|
||||
end
|
||||
if self.Function[sKey] then
|
||||
return self.Function[sKey]( SUNSTAMP )
|
||||
elseif self.SunStamp[sKey] then
|
||||
if self.SunStamp[sKey][SUNSTAMP] ~= nil then
|
||||
return self.SunStamp[sKey][SUNSTAMP]
|
||||
end
|
||||
-- This sunstamp isn't set, try and elevate stamp and check
|
||||
if SUNSTAMP >= SF_SKY_CEVIL then
|
||||
for i = SF_SKY_CEVIL + 1, SF_SKY_NIGHT do
|
||||
if self.SunStamp[sKey][i] then
|
||||
return self.SunStamp[sKey][i]
|
||||
end
|
||||
end
|
||||
else
|
||||
for i = SF_SKY_CEVIL - 1, SF_SKY_DAY, -1 do
|
||||
if self.SunStamp[sKey][i] then
|
||||
return self.SunStamp[sKey][i]
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif self.Static[sKey] then
|
||||
return self.Static[sKey], true
|
||||
elseif self.Dynamic[sKey] then
|
||||
return self.Dynamic[sKey]
|
||||
end
|
||||
if self.Name == "Clear" then return end
|
||||
-- Check if we inherit
|
||||
if not self.Inherit then return nil end
|
||||
if not Weathers[self.Inherit] then return nil end -- Inherit is invalid
|
||||
if in_list[self.Inherit] == true then -- Loop detected
|
||||
StormFox2.Warning("WeatherData loop detected! multiple occurences of : " .. self.Inherit)
|
||||
return
|
||||
end
|
||||
in_list[self.Name] = true
|
||||
local a,b,c,d,e = Weathers[self.Inherit]:Get(sKey, SUNSTAMP)
|
||||
in_list = {}
|
||||
return a,b,c,d,e
|
||||
end
|
||||
end
|
||||
|
||||
---Sets the terrain for the weather. This can also be a function that returns a terrain object.
|
||||
---@param zTerrain SF2Terrain|function
|
||||
---@shared
|
||||
function w_meta:SetTerrain( zTerrain )
|
||||
self:Set( "Terrain", zTerrain )
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindow( fFunc )
|
||||
self._RenderWindow = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindowRefract( fFunc )
|
||||
self._RenderWindowRefract = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindow64x64( fFunc )
|
||||
self._RenderWindow64x64 = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindowRefract64x64( fFunc )
|
||||
self._RenderWindowRefract64x64 = fFunc
|
||||
end
|
||||
|
||||
---Returns the "lightlevel" of the skybox in a range of 0-255.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Weather.GetLuminance()
|
||||
local Col = StormFox2.Mixer.Get("bottomColor") or Color(255,255,255)
|
||||
return 0.2126 * Col.r + 0.7152 * Col.g + 0.0722 * Col.b
|
||||
end
|
||||
|
||||
-- Load the weathers once lib is done.
|
||||
hook.Add("stormfox2.postlib", "stormfox2.loadweathers", function()
|
||||
StormFox2.Setting.AddSV("weather_damage",true,nil, "Weather")
|
||||
|
||||
|
||||
StormFox2.Setting.AddSV("moonsize",20,nil,"Effects", 5, 500)
|
||||
|
||||
hook.Run("stormfox2.preloadweather", w_meta)
|
||||
for _,fil in ipairs(file.Find("stormfox2/weathers/*.lua","LUA")) do
|
||||
local succ = pcall(include,"stormfox2/weathers/" .. fil)
|
||||
if SERVER and succ then
|
||||
AddCSLuaFile("stormfox2/weathers/" .. fil)
|
||||
end
|
||||
end
|
||||
StormFox2.Weather.Loaded = true
|
||||
hook.Run("stormfox2.postloadweather")
|
||||
end)
|
||||
570
lua/stormfox2/lib/sh_wind.lua
Normal file
570
lua/stormfox2/lib/sh_wind.lua
Normal file
@@ -0,0 +1,570 @@
|
||||
--[[
|
||||
| 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.Wind = StormFox2.Wind or {}
|
||||
local min,max,sqrt,abs = math.min,math.max,math.sqrt,math.abs
|
||||
local function ET(pos,pos2,mask,filter)
|
||||
local t = util.TraceLine( {
|
||||
start = pos,
|
||||
endpos = pos + pos2,
|
||||
mask = mask,
|
||||
filter = filter
|
||||
} )
|
||||
t.HitPos = t.HitPos or (pos + pos2)
|
||||
return t,t.HitSky
|
||||
end
|
||||
|
||||
-- Settings
|
||||
hook.Add("stormfox2.postlib", "stormfox2.windSettings",function()
|
||||
StormFox2.Setting.AddSV("windmove_players",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_foliate",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props",false,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_break",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_unweld",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_unfreeze",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_max",100,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_makedebris",true,nil,"Weather")
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.windSettings")
|
||||
end)
|
||||
|
||||
|
||||
if SERVER then
|
||||
hook.Add("stormfox2.postlib", "stormfox2.svWindInit",function()
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
for _,ent in ipairs( StormFox2.Ent.env_winds ) do
|
||||
ent:SetKeyValue('windradius',-1) -- Make global
|
||||
ent:SetKeyValue('maxgustdelay', 20)
|
||||
ent:SetKeyValue('mingustdelay', 10)
|
||||
ent:SetKeyValue('gustduration', 5)
|
||||
end
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.svWindInit")
|
||||
end)
|
||||
---Sets the wind force. Second argument is the lerp-time.
|
||||
---@param nForce number
|
||||
---@param nLerpTime? number
|
||||
---@server
|
||||
function StormFox2.Wind.SetForce( nForce, nLerpTime )
|
||||
StormFox2.Network.Set( "Wind", nForce, nLerpTime )
|
||||
end
|
||||
|
||||
---Sets the wind yaw. Second argument is the lerp-time.
|
||||
---@param nYaw number
|
||||
---@param nLerpTime? number
|
||||
---@server
|
||||
function StormFox2.Wind.SetYaw( nYaw, nLerpTime )
|
||||
StormFox2.Network.Set( "WindAngle", nYaw, nLerpTime )
|
||||
end
|
||||
end
|
||||
|
||||
---Returns the wind yaw-direction
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Wind.GetYaw()
|
||||
return StormFox2.Data.Get( "WindAngle", 0 )
|
||||
end
|
||||
|
||||
---Returns the wind force.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Wind.GetForce()
|
||||
return StormFox2.Data.Get( "Wind", 0 )
|
||||
end
|
||||
|
||||
-- Beaufort scale and Saffir–Simpson hurricane scale
|
||||
local bfs = {}
|
||||
bfs[0] = "sf_winddescription.calm"
|
||||
bfs[0.3] = "sf_winddescription.light_air"
|
||||
bfs[1.6] = "sf_winddescription.light_breeze"
|
||||
bfs[3.4] = "sf_winddescription.gentle_breeze"
|
||||
bfs[5.5] = "sf_winddescription.moderate_breeze"
|
||||
bfs[8] = "sf_winddescription.fresh_breeze"
|
||||
bfs[10.8] = "sf_winddescription.strong_breeze"
|
||||
bfs[13.9] = "sf_winddescription.near_gale"
|
||||
bfs[17.2] = "sf_winddescription.gale"
|
||||
bfs[20.8] = "sf_winddescription.strong_gale"
|
||||
bfs[24.5] = "sf_winddescription.storm"
|
||||
bfs[28.5] = "sf_winddescription.violent_storm"
|
||||
bfs[32.7] = "sf_winddescription.hurricane" -- Also known as cat 1
|
||||
bfs[43] = "sf_winddescription.cat2"
|
||||
bfs[50] = "sf_winddescription.cat3"
|
||||
bfs[58] = "sf_winddescription.cat4"
|
||||
bfs[70] = "sf_winddescription.cat5"
|
||||
local bfkey = table.GetKeys(bfs)
|
||||
table.sort(bfkey,function(a,b) return a < b end)
|
||||
|
||||
---Returns the current (or given wind in m/s), in a beaufort-scale and description.
|
||||
---@param ms? number
|
||||
---@return number
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Wind.GetBeaufort(ms)
|
||||
local n = ms or StormFox2.Wind.GetForce()
|
||||
local Beaufort, Description = 0, "sf_winddescription.calm"
|
||||
for k,kms in ipairs( bfkey ) do
|
||||
if kms <= n then
|
||||
Beaufort, Description = k - 1, bfs[ kms ]
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return Beaufort, Description
|
||||
end
|
||||
-- Spawning env_wind won't work. Therefor we need to use the cl_tree_sway_dir on the client if it's not on the map.
|
||||
if CLIENT then
|
||||
local function updateWind()
|
||||
if StormFox2.Map.HadClass( "env_wind" ) then return end
|
||||
local nw = math.min(StormFox2.Wind.GetForce() * 0.6, 21)
|
||||
local ra = math.rad( StormFox2.Data.Get( "WindAngle", 0 ) )
|
||||
local wx,wy = math.cos(ra) * nw,math.sin(ra) * nw
|
||||
RunConsoleCommand("cl_tree_sway_dir",wx,wy)
|
||||
end
|
||||
hook.Add("StormFox2.Wind.Change","StormFox2.Wind.CLFix",function(windNorm, wind)
|
||||
if not StormFox2.Setting.GetCache("windmove_foliate", true) then return end
|
||||
updateWind()
|
||||
end)
|
||||
StormFox2.Setting.Callback("windmove_foliate", function(b)
|
||||
if b then
|
||||
updateWind()
|
||||
else
|
||||
RunConsoleCommand("cl_tree_sway_dir",0,0)
|
||||
end
|
||||
end)
|
||||
else
|
||||
local function updateWind(nw, ang)
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
local min = nw * .6
|
||||
local max = nw * .8
|
||||
local gust = math.min(nw, 5.5)
|
||||
for _,ent in ipairs( StormFox2.Ent.env_winds ) do
|
||||
if not IsValid(ent) then continue end
|
||||
--print(ent, max, min ,gust)
|
||||
if ang then ent:Fire('SetWindDir', ang) end
|
||||
ent:SetKeyValue('minwind', min)
|
||||
ent:SetKeyValue('maxwind', max)
|
||||
ent:SetKeyValue('gustdirchange', math.max(0, 21 - nw))
|
||||
ent:SetKeyValue('maxgust', gust)
|
||||
ent:SetKeyValue('mingust', gust * .8)
|
||||
end
|
||||
end
|
||||
hook.Add("StormFox2.Wind.Change","StormFox2.Wind.SVFix",function(windNorm, wind)
|
||||
local nw = StormFox2.Wind.GetForce() * 2
|
||||
local ang = StormFox2.Data.Get( "WindAngle", 0 )
|
||||
updateWind(nw, ang)
|
||||
end)
|
||||
StormFox2.Setting.Callback("windmove_foliate", function(b)
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
local ang = StormFox2.Data.Get( "WindAngle", 0 )
|
||||
if not b then
|
||||
updateWind(0, ang)
|
||||
return
|
||||
end
|
||||
local nw = StormFox2.Wind.GetForce() * 2
|
||||
updateWind(nw, ang)
|
||||
end)
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Calculate and update the wind direction
|
||||
---------------------------------------------------------------------------]]
|
||||
local windNorm = Vector(0,0,-1)
|
||||
local windVec = Vector(0,0,0)
|
||||
local wind,windAng = 0,-1
|
||||
local function calcfunc()
|
||||
local owind = StormFox2.Data.Get("Wind",0)
|
||||
local nwind = owind * 0.2
|
||||
local nang = StormFox2.Data.Get("WindAngle",0)
|
||||
if nwind == wind and nang == windAng then return end -- Nothing changed
|
||||
wind = nwind
|
||||
windAng = nang
|
||||
windNorm = Angle( 90 - sqrt(wind) * 10 ,windAng,0):Forward()
|
||||
windVec = windNorm * wind
|
||||
windNorm:Normalize()
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Gets called when the wind changes.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run("StormFox2.Wind.Change", windNorm, owind)
|
||||
end
|
||||
|
||||
-- If the wind-data changes, is changing or is done changing. Reclaculate the wind.
|
||||
timer.Create("StormFox2.Wind.Update", 1, 0, function()
|
||||
if not StormFox2.Data.IsLerping("Wind") and not StormFox2.Data.IsLerping("WindAngle") then return end
|
||||
calcfunc()
|
||||
end)
|
||||
local function dataCheck(sKey,sVar)
|
||||
if sKey ~= "Wind" and sKey ~= "WindAngle" then return end
|
||||
calcfunc()
|
||||
end
|
||||
hook.Add("StormFox2.data.change","StormFox2.Wind.Calc",dataCheck)
|
||||
hook.Add("StormFox2.data.lerpend", "StormFox2.Wind.Calcfinish", dataCheck)
|
||||
|
||||
---Returns the wind norm.
|
||||
---@return Vector
|
||||
---@shared
|
||||
function StormFox2.Wind.GetNorm()
|
||||
return windNorm
|
||||
end
|
||||
|
||||
---Returns the wind vector.
|
||||
---@return Vector
|
||||
---@shared
|
||||
function StormFox2.Wind.GetVector()
|
||||
return windVec
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Checks if an entity is out in the wind (or rain). Caches the result for 1 second.
|
||||
---------------------------------------------------------------------------]]
|
||||
local function IsMaterialEmpty( t )
|
||||
return t.HitTexture == "TOOLS/TOOLSINVISIBLE" or t.HitTexture == "**empty**" or t.HitTexture == "TOOLS/TOOLSNODRAW"
|
||||
end
|
||||
local function ET_II(pos, vec, mask, filter) -- Ignore invisble brushes 'n stuff'
|
||||
local lastT
|
||||
for i = 1, 5 do
|
||||
local t, a = ET(pos, vec, mask, filter)
|
||||
if not IsMaterialEmpty(t) and t.Hit then return t, a end
|
||||
lastT = lastT or t
|
||||
pos = t.HitPos
|
||||
end
|
||||
lastT.HitSky = true
|
||||
return lastT
|
||||
end
|
||||
local max_dis = 32400
|
||||
|
||||
---Checks to see if the entity is in the wind.
|
||||
---@param eEnt userdata
|
||||
---@param bDont_cache? boolean
|
||||
---@return boolean IsInWind
|
||||
---@return Vector WindNorm
|
||||
---@shared
|
||||
function StormFox2.Wind.IsEntityInWind(eEnt,bDont_cache)
|
||||
if not IsValid(eEnt) then return end
|
||||
if not bDont_cache then
|
||||
if eEnt.sf_wind_var and (eEnt.sf_wind_var[2] or 0) > CurTime() then
|
||||
return eEnt.sf_wind_var[1],windNorm
|
||||
else
|
||||
eEnt.sf_wind_var = {}
|
||||
end
|
||||
end
|
||||
local pos = eEnt:OBBCenter() + eEnt:GetPos()
|
||||
local tr = ET_II(pos, windNorm * -640000, MASK_SHOT, eEnt)
|
||||
local hitSky = tr.HitSky
|
||||
local dis = tr.HitPos:DistToSqr( pos )
|
||||
if not hitSky and dis >= max_dis then -- So far away. The wind would had gone around. Check if we're outside.
|
||||
local tr = ET(pos,Vector(0,0,640000),MASK_SHOT,eEnt)
|
||||
hitSky = tr.HitSky
|
||||
end
|
||||
if not bDont_cache then
|
||||
eEnt.sf_wind_var[1] = hitSky
|
||||
eEnt.sf_wind_var[2] = CurTime() + 1
|
||||
end
|
||||
return hitSky,windNorm
|
||||
end
|
||||
|
||||
-- Wind sounds
|
||||
if CLIENT then
|
||||
local windSnd = -1 -- -1 = none, 0 = outside, 0+ Distance to outside
|
||||
local windGusts = {}
|
||||
local maxVol = 1.5
|
||||
local function AddGuest( snd, vol, duration )
|
||||
if windGusts[snd] then return end
|
||||
if not duration then duration = SoundDuration( snd ) end
|
||||
windGusts[snd] = {vol * 0.4, CurTime() + duration - 1}
|
||||
end
|
||||
|
||||
timer.Create("StormFox2.Wind.Snd", 1, 0, function()
|
||||
windSnd = -1
|
||||
if StormFox2.Wind.GetForce() <= 0 then return end
|
||||
local env = StormFox2.Environment.Get()
|
||||
if not env or (not env.outside and not env.nearest_outside) then return end
|
||||
if not env.outside and env.nearest_outside then
|
||||
local view = StormFox2.util.RenderPos()
|
||||
windSnd = StormFox2.util.RenderPos():Distance(env.nearest_outside)
|
||||
else
|
||||
windSnd = 0
|
||||
end
|
||||
-- Guests
|
||||
local vM = (400 - windSnd) / 400
|
||||
if vM <= 0 then return end
|
||||
local wForce = StormFox2.Wind.GetForce()
|
||||
if math.random(50) > 40 then
|
||||
if wForce > 17 and math.random(1,2) > 1 then
|
||||
AddGuest("ambient/wind/windgust.wav",math.Rand(0.8, 1) * vM)
|
||||
elseif wForce > 14 and wForce < 30 then
|
||||
AddGuest("ambient/wind/wind_med" .. math.random(1,2) .. ".wav", math.min(maxVol, wForce / 30) * vM)
|
||||
end
|
||||
end
|
||||
if wForce > 27 and math.random(50) > 30 then
|
||||
AddGuest("ambient/wind/windgust_strong.wav",math.min(maxVol, wForce / 30) * vM)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cold "empty" wind: ambience/wind1.wav
|
||||
-- ambient/wind/wind1.wav
|
||||
-- ambient/wind/wind_rooftop1.wav
|
||||
-- ambient/wind/wind1.wav
|
||||
-- StormFox2.Ambience.ForcePlay
|
||||
hook.Add("StormFox2.Ambiences.OnSound", "StormFox2.Ambiences.Wind", function()
|
||||
if windSnd < 0 then return end -- No wind
|
||||
local wForce = StormFox2.Wind.GetForce() * 0.25
|
||||
local vM = (400 - windSnd) / 400
|
||||
if vM <= 0 then return end
|
||||
-- Main loop
|
||||
StormFox2.Ambience.ForcePlay( "ambient/wind/wind_rooftop1.wav", math.min((wForce - 1) / 35, maxVol) * vM * 0.8, math.min(1.2, 0.9 + wForce / 100) )
|
||||
-- Wind gusts
|
||||
for snd,data in pairs(windGusts) do
|
||||
if data[2] <= CurTime() then
|
||||
windGusts[snd] = nil
|
||||
else
|
||||
StormFox2.Ambience.ForcePlay( snd, 0.2 * data[1] + math.Rand(0, 0.1) )
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- Flag models
|
||||
local flags = {}
|
||||
local flag_models = {}
|
||||
flag_models["models/props_fairgrounds/fairgrounds_flagpole01.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole_american.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole_american_tattered.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole.mdl"] = 90
|
||||
flag_models["models/mapmodels/flags.mdl"] = 0
|
||||
flag_models["models/props/de_cbble/cobble_flagpole.mdl"] = 180
|
||||
flag_models["models/props/de_cbble/cobble_flagpole_2.mdl"] = 225
|
||||
flag_models["models/props/props_gameplay/capture_flag.mdl"] = 270
|
||||
flag_models["models/props_medieval/pendant_flag/pendant_flag.mdl"] = 0
|
||||
flag_models["models/props_moon/parts/moon_flag.mdl"] = 0
|
||||
local function FlagInit()
|
||||
-- Check if there are any flags on the map
|
||||
for _,ent in pairs(ents.GetAll()) do
|
||||
if not ent:CreatedByMap() then continue end
|
||||
-- Check the angle
|
||||
if math.abs(ent:GetAngles():Forward():Dot(Vector(0,0,1))) > 5 then continue end
|
||||
if not flag_models[ent:GetModel()] then continue end
|
||||
table.insert(flags,ent)
|
||||
end
|
||||
if #flags > 0 then -- Only add the hook if there are flags on the map.
|
||||
hook.Add("StormFox2.data.change","StormFox2.flagcontroller",function(key,var)
|
||||
if key == "WindAngle" then
|
||||
--print("Windang", var)
|
||||
for _,ent in ipairs(flags) do
|
||||
if not IsValid(ent) then continue end
|
||||
local y = flag_models[ent:GetModel()] or 0
|
||||
ent:SetAngles(Angle(0,var + y,0))
|
||||
end
|
||||
elseif key == "Wind" then
|
||||
--print("Wind", var)
|
||||
for _,ent in ipairs(flags) do
|
||||
if not IsValid(ent) then continue end
|
||||
ent:SetPlaybackRate(math.Clamp(var / 7,0.5,10))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
hook.Add("StormFox2.PostEntityScan", "StormFox2.Wind.FlagInit", FlagInit)
|
||||
end
|
||||
|
||||
if CLIENT then return end
|
||||
-- Wind movment
|
||||
local function windMove(ply, mv, cmd )
|
||||
if not StormFox2.Setting.GetCache("windmove_players") then return end
|
||||
if( ply:GetMoveType() != MOVETYPE_WALK ) then return end
|
||||
local wF = (StormFox2.Wind.GetForce() - 15) / 11
|
||||
if wF <= 0 then return end
|
||||
if not StormFox2.Wind.IsEntityInWind(ply) then return end -- Not in wind
|
||||
-- Calc windforce
|
||||
local r = math.rad( StormFox2.Wind.GetYaw() - ply:GetAngles().y )
|
||||
local fS = math.cos( r ) * wF
|
||||
local sS = math.sin( r ) * wF
|
||||
|
||||
if mv:GetForwardSpeed() == 0 and mv:GetSideSpeed() == 0 then -- Not moving
|
||||
--mv:SetSideSpeed( - sS / 10) Annoying
|
||||
--mv:SetForwardSpeed( - fS / 10)
|
||||
else
|
||||
-- GetForwardMove() returns 10000y. We need to figure out the speed first.
|
||||
local running, walking = mv:KeyDown( IN_SPEED ), mv:KeyDown( IN_WALK )
|
||||
local speed = running and ply:GetRunSpeed() or walking and ply:GetSlowWalkSpeed() or ply:GetWalkSpeed()
|
||||
|
||||
local forward = math.Clamp(mv:GetForwardSpeed(), -speed, speed)
|
||||
local side = math.Clamp(mv:GetSideSpeed(), -speed, speed)
|
||||
if forward~=0 and side~=0 then
|
||||
forward = forward * .7
|
||||
side = side * .7
|
||||
end
|
||||
-- Now we modify them. We don't want to move back.
|
||||
if forward > 0 and fS < 0 then
|
||||
forward = math.max(0, forward / -fS)
|
||||
elseif forward < 0 and fS > 0 then
|
||||
forward = math.min(0, forward / fS)
|
||||
end
|
||||
if side > 0 and sS > 0 then
|
||||
side = math.max(0, side / sS)
|
||||
forward = forward + fS * 20
|
||||
elseif side < 0 and sS < 0 then
|
||||
side = math.min(0, side / -sS)
|
||||
forward = forward + fS * 20
|
||||
end
|
||||
-- Apply the new speed
|
||||
mv:SetForwardSpeed( forward )
|
||||
cmd:SetForwardMove( forward )
|
||||
mv:SetSideSpeed( side )
|
||||
cmd:SetSideMove( side )
|
||||
end
|
||||
end
|
||||
hook.Add("SetupMove", "windtest", windMove)
|
||||
|
||||
-- Wind proppush
|
||||
local move_list = {
|
||||
["rpg_missile"] = true,
|
||||
["npc_grenade_frag"] = true,
|
||||
["npc_grenade_bugbait"] = true, -- Doesn't work
|
||||
["gmod_hands"] = false,
|
||||
["gmod_tool"] = false
|
||||
}
|
||||
local function CanMoveClass( ent )
|
||||
if( IsValid( ent:GetParent()) ) then return end
|
||||
local class = ent:GetClass()
|
||||
if( move_list[class] == false ) then return false end
|
||||
return string.match(class,"^prop_") or string.match(class,"^gmod_") or move_list[class]
|
||||
end
|
||||
|
||||
local function ApplyWindEffect( ent, wind, windnorm )
|
||||
if ent:GetPersistent() then return end
|
||||
if(wind < 5) then return end
|
||||
-- Make a toggle
|
||||
local vol
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if(not phys or not IsValid(phys)) then -- No physics
|
||||
return
|
||||
end
|
||||
vol = phys:GetVolume() or 15
|
||||
-- Check Move
|
||||
local windPush = windnorm * 1.37 * vol * .66
|
||||
--windnorm * 5.92 * (vol / 50)
|
||||
local windRequ = phys:GetInertia()
|
||||
windRequ = max(windRequ.x,windRequ.y)
|
||||
if max(abs(windPush.x),abs(windPush.y)) < windRequ then -- Can't move
|
||||
return
|
||||
end
|
||||
local class = ent:GetClass()
|
||||
if( class != "npc_grenade_frag") then
|
||||
windPush.x = math.Clamp(windPush.x, -5500, 5500)
|
||||
windPush.y = math.Clamp(windPush.y, -5500, 5500)
|
||||
end
|
||||
-- Unfreeze
|
||||
if(wind > 20) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_unfreeze", true) ) then
|
||||
if not phys:IsMoveable() then
|
||||
phys:EnableMotion(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Unweld
|
||||
if(wind > 30) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_unweld", true) ) then
|
||||
if constraint.FindConstraint( ent, "Weld" ) and math.random(1, 15) < 2 then
|
||||
ent:EmitSound("physics/wood/wood_box_break" .. math.random(1,2) .. ".wav")
|
||||
constraint.RemoveConstraints( ent, "Weld" )
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Move
|
||||
phys:Wake()
|
||||
phys:ApplyForceCenter(Vector(windPush.x, windPush.y, math.max(phys:GetVelocity().z, 0)))
|
||||
-- Break
|
||||
if(wind > 40) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_break", true) ) then
|
||||
if not ent:IsVehicle() and (ent._sfnext_dmg or 0) <= CurTime() and ent:GetClass() != "npc_grenade_frag" then
|
||||
ent._sfnext_dmg = CurTime() + 0.5
|
||||
ent:TakeDamage(ent:Health() / 10 + 2,game.GetWorld(),game.GetWorld())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local move_tab = {}
|
||||
local current_prop = 0
|
||||
local function AddEntity( ent )
|
||||
if( ent._sfwindcan or 0 ) > CurTime() then return end
|
||||
if( StormFox2.Setting.GetCache("windmove_props_max", 50) <= table.Count(move_tab) ) then return end -- Too many props moving atm
|
||||
move_tab[ ent ] = CurTime()
|
||||
--ApplyWindEffect( ent, StormFox2.Wind.GetForce(), StormFox2.Wind.GetNorm() )
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated","StormFox.Wind.PropMove",function(ent)
|
||||
if( not StormFox2.Setting.GetCache("windmove_props", false) ) then return end
|
||||
if( not IsValid(ent) ) then return end
|
||||
if( not CanMoveClass( ent ) ) then return end
|
||||
AddEntity( ent )
|
||||
end)
|
||||
|
||||
local scanID = 0
|
||||
local function ScanProps()
|
||||
local t = ents.GetAll()
|
||||
if( #t < scanID) then
|
||||
scanID = 0
|
||||
end
|
||||
for i = scanID, math.min(#t, scanID + 30) do
|
||||
local ent = t[i]
|
||||
if(not IsValid( ent )) then break end
|
||||
if ent:GetPersistent() then continue end
|
||||
if not CanMoveClass( ent ) then continue end
|
||||
if move_tab[ ent ] then continue end -- Already added
|
||||
if not StormFox2.Wind.IsEntityInWind( ent ) then continue end -- Not in wind
|
||||
AddEntity( ent )
|
||||
end
|
||||
scanID = scanID + 30
|
||||
end
|
||||
|
||||
local next_loop = 0 -- We shouldn't run this on think if there arae too few props
|
||||
hook.Add("Think","StormFox.Wind.EffectProps",function()
|
||||
if( not StormFox2.Setting.GetCache("windmove_props", false) ) then return end
|
||||
if( next_loop > CurTime() ) then return end
|
||||
next_loop = CurTime() + (game.SinglePlayer() and 0.1 or 0.2)
|
||||
-- Scan on all entities. This would be slow. But we're only looking at 30 entities at a time.
|
||||
ScanProps()
|
||||
|
||||
local t = table.GetKeys( move_tab )
|
||||
if(#t < 1) then return end
|
||||
local wind = StormFox2.Wind.GetForce()
|
||||
-- Check if there is wind
|
||||
if( wind < 10) then
|
||||
table.Empty( move_tab )
|
||||
return
|
||||
end
|
||||
if( current_prop > #t) then
|
||||
current_prop = 1
|
||||
end
|
||||
local wind, c_windnorm = StormFox2.Wind.GetForce(), StormFox2.Wind.GetNorm()
|
||||
local windnorm = Vector(c_windnorm.x, c_windnorm.y, 0)
|
||||
local c = CurTime()
|
||||
for i = current_prop, math.min(#t, current_prop + 30) do
|
||||
local ent = t[i]
|
||||
if(not ent) then
|
||||
break
|
||||
end
|
||||
-- Check if valid
|
||||
if(not IsValid( ent ) or not StormFox2.Wind.IsEntityInWind( ent )) then
|
||||
move_tab[ent] = nil
|
||||
continue
|
||||
end
|
||||
-- Check if presistence
|
||||
if ent:GetPersistent() then continue end
|
||||
-- If the entity has been in the wind for over 10 seconds, try and move on and see if we can pick up something else
|
||||
if(move_tab[ ent ] < c - 10) then
|
||||
ent._sfwindcan = c + math.random(20, 30)
|
||||
if(StormFox2.Setting.GetCache("windmove_props_makedebris", true)) then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
|
||||
end
|
||||
move_tab[ent] = nil
|
||||
continue
|
||||
end
|
||||
ApplyWindEffect( ent, wind, windnorm )
|
||||
end
|
||||
current_prop = current_prop + 30
|
||||
end)
|
||||
37
lua/stormfox2/lib/sv_content.lua
Normal file
37
lua/stormfox2/lib/sv_content.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Adds SF content
|
||||
if not StormFox2.WorkShopVersion then
|
||||
local i = 0
|
||||
local function AddDir(dir,dirlen)
|
||||
if not dirlen then dirlen = dir:len() end
|
||||
local files, folders = file.Find(dir .. "/*", "GAME")
|
||||
for _, fdir in ipairs(folders) do
|
||||
if fdir ~= ".svn" then
|
||||
AddDir(dir .. "/" .. fdir)
|
||||
end
|
||||
end
|
||||
for k, v in ipairs(files) do
|
||||
local fil = dir .. "/" .. v
|
||||
resource.AddFile(fil)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
AddDir("materials/stormfox2")
|
||||
AddDir("sound/stormfox2")
|
||||
AddDir("models/stormfox2")
|
||||
StormFox2.Msg("Added " .. i .. " content files.")
|
||||
-- Add the workshop
|
||||
else
|
||||
resource.AddWorkshop(string.match(StormFox2.WorkShopURL, "%d+$"))
|
||||
StormFox2.Msg("Added content files from workshop.")
|
||||
end
|
||||
80
lua/stormfox2/lib/sv_entities.lua
Normal file
80
lua/stormfox2/lib/sv_entities.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Creates or finds map entities
|
||||
Hook:
|
||||
StormFox2.PostEntityScan Gets called after StormFox have located the map entities.
|
||||
Convar:
|
||||
sf_enable_mapsupport
|
||||
---------------------------------------------------------------------------]]
|
||||
StormFox2.Ent = {}
|
||||
CreateConVar("sf_enable_mapsupport","1",{FCVAR_REPLICATED,FCVAR_ARCHIVE},"StormFox2.setting.mapsupport")
|
||||
-- Find or creat entities
|
||||
local function GetOrCreate(str,only_get)
|
||||
local l = ents.FindByClass(str)
|
||||
local con = GetConVar("sf_enable_mapsupport")
|
||||
if #l > 0 then
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(55,255,55), "Found", Color( 255, 255, 255), "\n" )
|
||||
return l
|
||||
end
|
||||
if not con:GetBool() or only_get then -- Disabled mapsupport or don't create
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(255,55,55), "Not found", Color( 255, 255, 255), "\n" )
|
||||
return
|
||||
end
|
||||
local ent = ents.Create(str)
|
||||
ent:Spawn();
|
||||
ent:Activate();
|
||||
ent._sfcreated = true
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(155,155,255), "Created", Color( 255, 255, 255), "\n" )
|
||||
return {ent}
|
||||
end
|
||||
-- We need to use this function, as some entities spawn regardless of what the map has.
|
||||
local function findEntities()
|
||||
StormFox2.Msg( "Scanning mapentities ..." )
|
||||
local tSunlist = ents.FindByClass( "env_sun" )
|
||||
for i = 1, #tSunlist do -- Remove any env_suns, there should be only one but who knows
|
||||
tSunlist[ i ]:Fire( "TurnOff" )
|
||||
end
|
||||
StormFox2.Ent.env_skypaints = GetOrCreate( "env_skypaint" )
|
||||
StormFox2.Ent.light_environments = GetOrCreate( "light_environment", true)
|
||||
StormFox2.Ent.env_fog_controllers = GetOrCreate( "env_fog_controller" )
|
||||
StormFox2.Ent.shadow_controls = GetOrCreate( "shadow_control", true )
|
||||
StormFox2.Ent.env_tonemap_controllers = GetOrCreate("env_tonemap_controller", true )
|
||||
StormFox2.Ent.env_winds = GetOrCreate("env_wind", true ) -- Can't spawn the wind controller without problems
|
||||
StormFox2.Ent.env_tonemap_controller = GetOrCreate( "env_tonemap_controller", true)
|
||||
-- Kill TF2 sun
|
||||
for k,v in ipairs(ents.FindByModel("models/props_skybox/sunnoon.mdl")) do
|
||||
if v:IsValid() then
|
||||
v:SetNoDraw( true )
|
||||
end
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Gets called when StormFox has handled map-entities.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run( "StormFox2.PostEntityScan" )
|
||||
end
|
||||
-- If this is first run, wait for InitPostEntity.
|
||||
hook.Add("StormFox2.InitPostEntity","StormFox2.Entities",findEntities)
|
||||
-- Tell clients about explosions
|
||||
util.AddNetworkString("StormFox2.entity.explosion")
|
||||
hook.Add("EntityRemoved","StormFox2.Entitys.Explosion",function(ent)
|
||||
if ent:GetClass() ~= "env_explosion" then return end
|
||||
local t = ent:GetKeyValues()
|
||||
net.Start("StormFox2.entity.explosion")
|
||||
net.WriteVector(ent:GetPos())
|
||||
net.WriteUInt(t.iRadiusOverride or t.iMagnitude, 16)
|
||||
net.WriteUInt(t.iMagnitude, 16)
|
||||
net.SendPVS(ent:GetPos())
|
||||
hook.Run("StormFox2.Entitys.OnExplosion", ent:GetPos(), t.iRadiusOverride, t.iMagnitude)
|
||||
end)
|
||||
139
lua/stormfox2/lib/sv_perlin.lua
Normal file
139
lua/stormfox2/lib/sv_perlin.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
Implemented as described here:
|
||||
http://flafla2.github.io/2014/08/09/perlinnoise.html
|
||||
|
||||
Copied from: https://gist.github.com/SilentSpike/25758d37f8e3872e1636d90ad41fe2ed
|
||||
]]--
|
||||
|
||||
local floor,band,clamp,max = math.floor,bit.band,math.Clamp,math.max
|
||||
|
||||
perlin = {}
|
||||
local p = {}
|
||||
|
||||
-- Hash lookup table as defined by Ken Perlin
|
||||
-- This is a randomly arranged array of all numbers from 0-255 inclusive
|
||||
local permutation = {151,160,137,91,90,15,
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
}
|
||||
-- p is used to hash unit cube coordinates to [0, 255]
|
||||
for i = 0,255 do
|
||||
-- Convert to 0 based index table
|
||||
p[i] = permutation[i + 1]
|
||||
-- Repeat the array to avoid buffer overflow in hash function
|
||||
p[i + 256] = permutation[i + 1]
|
||||
end
|
||||
-- Functions
|
||||
local dot_product = {
|
||||
[0x0] = function(x,y,z) return x + y end,
|
||||
[0x1] = function(x,y,z) return -x + y end,
|
||||
[0x2] = function(x,y,z) return x - y end,
|
||||
[0x3] = function(x,y,z) return -x - y end,
|
||||
[0x4] = function(x,y,z) return x + z end,
|
||||
[0x5] = function(x,y,z) return -x + z end,
|
||||
[0x6] = function(x,y,z) return x - z end,
|
||||
[0x7] = function(x,y,z) return -x - z end,
|
||||
[0x8] = function(x,y,z) return y + z end,
|
||||
[0x9] = function(x,y,z) return -y + z end,
|
||||
[0xA] = function(x,y,z) return y - z end,
|
||||
[0xB] = function(x,y,z) return -y - z end,
|
||||
[0xC] = function(x,y,z) return y + x end,
|
||||
[0xD] = function(x,y,z) return -y + z end,
|
||||
[0xE] = function(x,y,z) return y - x end,
|
||||
[0xF] = function(x,y,z) return -y - z end
|
||||
}
|
||||
local function grad(hash, x, y, z)
|
||||
return dot_product[band(hash,0xF)](x,y,z)
|
||||
end
|
||||
local function fade(t)
|
||||
return t * t * t * (t * (t * 6 - 15) + 10)
|
||||
end
|
||||
local function lerp(t, a, b)
|
||||
return a + t * (b - a)
|
||||
end
|
||||
function perlin.noise(x, y, z, zoom) -- [-1 , 1]
|
||||
zoom = zoom or 100
|
||||
x = x / zoom
|
||||
y = y and y / zoom or 0
|
||||
z = z and z / zoom or 0
|
||||
|
||||
-- Calculate the "unit cube" that the point asked will be located in
|
||||
local xi = floor(x) % 256
|
||||
local yi = floor(y) % 256
|
||||
local zi = floor(z) % 256
|
||||
|
||||
-- Next we calculate the location (from 0 to 1) in that cube
|
||||
x = x - floor(x)
|
||||
y = y - floor(y)
|
||||
z = z - floor(z)
|
||||
|
||||
-- We also fade the location to smooth the result
|
||||
local u = fade(x)
|
||||
local v = fade(y)
|
||||
local w = fade(z)
|
||||
|
||||
-- Hash all 8 unit cube coordinates surrounding input coordinate
|
||||
local A = p[xi ] + yi
|
||||
local AA = p[A ] + zi
|
||||
local AB = p[A + 1 ] + zi
|
||||
local AAA = p[ AA ]
|
||||
local ABA = p[ AB ]
|
||||
local AAB = p[ AA + 1 ]
|
||||
local ABB = p[ AB + 1 ]
|
||||
|
||||
local B = p[xi + 1] + yi
|
||||
local BA = p[B ] + zi
|
||||
local BB = p[B + 1 ] + zi
|
||||
local BAA = p[ BA ]
|
||||
local BBA = p[ BB ]
|
||||
local BAB = p[ BA + 1 ]
|
||||
local BBB = p[ BB + 1 ]
|
||||
|
||||
-- Take the weighted average between all 8 unit cube coordinates
|
||||
return lerp(w,
|
||||
lerp(v,
|
||||
lerp(u,
|
||||
grad(AAA,x,y,z),
|
||||
grad(BAA,x-1,y,z)
|
||||
),
|
||||
lerp(u,
|
||||
grad(ABA,x,y-1,z),
|
||||
grad(BBA,x-1,y-1,z)
|
||||
)
|
||||
),
|
||||
lerp(v,
|
||||
lerp(u,
|
||||
grad(AAB,x,y,z-1), grad(BAB,x-1,y,z-1)
|
||||
),
|
||||
lerp(u,
|
||||
grad(ABB,x,y-1,z-1), grad(BBB,x-1,y-1,z-1)
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
function perlin.range(x, y ,z, zoom) -- [0 - 1]
|
||||
return (1 + perlinnoise(x, y, z, zoom)) / 2
|
||||
end
|
||||
function perlin.rangeSub(x, y, z , zoom, n)
|
||||
return max(0,(perlin.range(x, y ,z, zoom) - n ) / (1 - n))
|
||||
end
|
||||
126
lua/stormfox2/sh_cwi.lua
Normal file
126
lua/stormfox2/sh_cwi.lua
Normal file
@@ -0,0 +1,126 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Commen Weather Interface.
|
||||
Still WIP!
|
||||
]]
|
||||
|
||||
local Version = 0.02
|
||||
if CWI and CWI.Version > Version then return end
|
||||
|
||||
CWI = {}
|
||||
CWI.Version = Version
|
||||
CWI.NotSupported = 12345
|
||||
CWI.WeatherMod = "StormFox 2"
|
||||
|
||||
-- Time
|
||||
if SERVER then
|
||||
--[[
|
||||
Sets the time by using a number between 0 - 1440
|
||||
720 being midday
|
||||
]]
|
||||
function CWI.SetTime( num )
|
||||
StormFox2.Time.Set( num )
|
||||
end
|
||||
--[[
|
||||
Sets the timespeed
|
||||
1 = real time
|
||||
60 = 60x real time speed
|
||||
]]
|
||||
CWI.SetTimeSpeed = StormFox2.Time.SetSpeed
|
||||
end
|
||||
--[[
|
||||
Returns the time as a number between 0 - 1440
|
||||
720 being midday
|
||||
]]
|
||||
CWI.GetTime = StormFox2.Time.Get
|
||||
--[[
|
||||
Returns the timespeed.
|
||||
]]
|
||||
CWI.GetTimeSpeed = StormFox2.Time.GetSpeed
|
||||
-- Easy day/night variables
|
||||
CWI.IsDay = function() return StormFox2.Sun.IsUp() end
|
||||
CWI.IsNight = function() return not StormFox2.Sun.IsUp() end
|
||||
|
||||
-- Weather
|
||||
--[[
|
||||
Sets the weather
|
||||
]]
|
||||
if SERVER then
|
||||
function CWI.SetWeather( str )
|
||||
str = string.upper(str[1]) .. string.sub(str, 2):lower()
|
||||
StormFox2.Weather.Set( str )
|
||||
end
|
||||
end
|
||||
-- Returns the current weather
|
||||
function CWI.GetWeather()
|
||||
return StormFox2.Weather.GetCurrent().Name:lower()
|
||||
end
|
||||
-- Returns a list of all weather-types
|
||||
CWI.DefaultWeather = "clear"
|
||||
function CWI.GetWeathers()
|
||||
local t = {}
|
||||
for _, str in ipairs( StormFox2.Weather.GetAll() ) do
|
||||
table.insert(t, StormFox2.Weather.Get(str).Name:lower())
|
||||
end
|
||||
return t
|
||||
end
|
||||
-- Downfall
|
||||
CWI.IsRaining = StormFox2.Weather.IsRaining
|
||||
CWI.IsSnowing = StormFox2.Weather.IsSnowing
|
||||
|
||||
-- Time Functions
|
||||
function CWI.GetHours( bUse12Hour )
|
||||
if not bUse12Hour then return math.floor( CWI.GetTime() / 60 ) end
|
||||
local h = math.floor( CWI.GetTime() / 60 )
|
||||
local b = ( h < 12 or h == 0 ) and "AM" or "PM"
|
||||
if h == 0 then
|
||||
h = 12
|
||||
elseif h > 12 then
|
||||
h = h - 12
|
||||
end
|
||||
return h, b
|
||||
end
|
||||
function CWI.GetMinutes()
|
||||
return math.floor( CWI.GetTime() % 60 )
|
||||
end
|
||||
function CWI.GetSeconds()
|
||||
return math.floor( CWI.GetTime() % 1 ) * 60
|
||||
end
|
||||
function CWI.TimeToString( bUse12Hour )
|
||||
local h, e = CWI.GetHours( bUse12Hour )
|
||||
return h .. ":" .. CWI.GetMinutes() .. (e and " " .. e or "")
|
||||
end
|
||||
|
||||
--[[
|
||||
Hook: CWI.NewWeather, weatherName
|
||||
]]
|
||||
local lastW = "Unknown"
|
||||
hook.Add("StormFox2.weather.postchange", "CWI.CallNewWeather", function( sName )
|
||||
if lastW == sName then return end
|
||||
lastW = sName
|
||||
hook.Run("CWI.NewWeather", sName:lower())
|
||||
end)
|
||||
|
||||
--[[
|
||||
Hook: CWI.NewDay, dayNumber 0 - 365
|
||||
]]
|
||||
hook.Add("StormFox2.Time.NextDay", "CWI.CallNewDay", function( )
|
||||
hook.Run("CWI.NewDay")
|
||||
end)
|
||||
|
||||
--[[
|
||||
Hook: CWI.Init
|
||||
]]
|
||||
hook.Add("stormfox2.postinit", "SFCWI.Init", function()
|
||||
hook.Run("CWI.Init")
|
||||
end)
|
||||
152
lua/stormfox2/vgui/cl_newvgui.lua
Normal file
152
lua/stormfox2/vgui/cl_newvgui.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- SF_TextEntry
|
||||
-- Supports for temperature conversion and other things
|
||||
do
|
||||
local PANEL = {}
|
||||
AccessorFunc( PANEL, "m_bTemp", "Temperature" )
|
||||
AccessorFunc( PANEL, "m_sUnity", "Unit" )
|
||||
local function convFromClient( val )
|
||||
return StormFox2.Temperature.Convert(StormFox2.Temperature.GetDisplayType(),nil ,tonumber(val))
|
||||
end
|
||||
local function convToClient( val )
|
||||
return StormFox2.Temperature.Convert(nil, StormFox2.Temperature.GetDisplayType() ,tonumber(val))
|
||||
end
|
||||
-- Hacky solution to units.
|
||||
local _oldDTET = vgui.GetControlTable("DTextEntry").DrawTextEntryText
|
||||
function PANEL:DrawTextEntryText( ... )
|
||||
if not self:GetUnit() then
|
||||
return _oldDTET( self, ... )
|
||||
end
|
||||
local s = self:GetText()
|
||||
self:SetText(s .. self:GetUnit())
|
||||
_oldDTET( self, ... )
|
||||
self:SetText(s)
|
||||
end
|
||||
-- Converts the text displayed and typed unto the temperature unit.
|
||||
function PANEL:SetTemperature( b )
|
||||
self.m_bTemp = b
|
||||
if b then
|
||||
self:SetUnit( StormFox2.Temperature.GetDisplaySymbol() )
|
||||
else
|
||||
self:SetUnit(nil)
|
||||
end
|
||||
end
|
||||
-- Overwrite the values for celcius.
|
||||
function PANEL:SetValue( strValue )
|
||||
if ( vgui.GetKeyboardFocus() == self ) then return end
|
||||
local CaretPos = self:GetCaretPos()
|
||||
strValue = self.m_bTemp and convToClient(strValue) or strValue
|
||||
self:SetText( strValue )
|
||||
self:OnValueChange( strValue )
|
||||
self:SetCaretPos( CaretPos )
|
||||
end
|
||||
function PANEL:UpdateConvarValue()
|
||||
self:ConVarChanged( self.m_bTemp and convFromClient(self:GetValue()) or self:GetValue() )
|
||||
end
|
||||
derma.DefineControl( "SF_TextEntry", "SF TextEntry", PANEL, "TextEntry" )
|
||||
end
|
||||
|
||||
-- SF_TextBox
|
||||
-- Wraos text
|
||||
do
|
||||
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, th
|
||||
end
|
||||
local PANEL = {}
|
||||
function PANEL:PerformLayout(w, h)
|
||||
local text, lines, th = wrapText(self:GetText(), self:GetWide() - self._b:GetWide())
|
||||
self._d:SetText(text)
|
||||
self._d:SizeToContents()
|
||||
local nh = math.max( th, lines * th)
|
||||
if nh > h then
|
||||
self:SetTall( math.max( th, lines * th) )
|
||||
end
|
||||
end
|
||||
derma.DefineControl( "SF_TextBox", "SF TextBox", PANEL, "DLabel" )
|
||||
end
|
||||
|
||||
-- SF_Slider
|
||||
-- Doesn't spam convar
|
||||
do
|
||||
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 PANEL = {}
|
||||
AccessorFunc( PANEL, "m_max", "Max" )
|
||||
AccessorFunc( PANEL, "m_min", "Min" )
|
||||
AccessorFunc( PANEL, "m_fDecimal", "Decimals" )
|
||||
AccessorFunc( PANEL, "m_bLiveUpdate", "UpdateLive" )
|
||||
AccessorFunc( PANEL, "m_fFloatValue", "FloatValue" )
|
||||
Derma_Install_Convar_Functions( PANEL )
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetMin( 0 )
|
||||
self:SetMax( 10 )
|
||||
self:SetDecimals( 2 )
|
||||
self:SetFloatValue( 1.5 )
|
||||
self:SetUpdateLive( false )
|
||||
self:SetText("")
|
||||
end
|
||||
function PANEL:SetValue( val )
|
||||
local val = tonumber( val )
|
||||
if ( val == nil ) then return end
|
||||
if ( val == self:GetFloatValue() ) then return end
|
||||
val = math.Round(val, self:GetDecimals())
|
||||
self:SetFloatValue( val )
|
||||
self:OnValueChanged( val )
|
||||
self:UpdateConVar()
|
||||
end
|
||||
function PANEL:Think()
|
||||
if ( !self:GetActive() ) then
|
||||
self:ConVarNumberThink()
|
||||
end
|
||||
if self._wdown and not self:IsDown() then
|
||||
self:ConVarChanged( self:GetFloatValue() )
|
||||
self._wdown = false
|
||||
elseif self:IsDown() and self._knob then
|
||||
local w_f = math.Clamp((self:LocalCursorPos() - 7) / (self:GetWide() - 15), 0, 1)
|
||||
local r = self:GetMax() - self:GetMin()
|
||||
self:SetValue(self:GetMin() + w_f * r)
|
||||
self._wdown = true
|
||||
end
|
||||
end
|
||||
function PANEL:Paint( w, h )
|
||||
derma.SkinHook( "Paint", "Slider", panel, w, h )
|
||||
local sw = w - 15
|
||||
paintKnob(self,sw * self.m_fSlideX,0)
|
||||
end
|
||||
derma.DefineControl("SF_Slider", "A simple slider", PANEL, "DButton")
|
||||
end
|
||||
|
||||
156
lua/stormfox2/weathers/clear.lua
Normal file
156
lua/stormfox2/weathers/clear.lua
Normal file
@@ -0,0 +1,156 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Clear weather. This is the default weather
|
||||
|
||||
local clear = StormFox2.Weather.Add( "Clear" )
|
||||
|
||||
local windy = 8
|
||||
|
||||
-- Description
|
||||
if CLIENT then
|
||||
function clear:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
local b_windy = StormFox2.Wind.GetBeaufort(nWind) >= windy
|
||||
if b_windy then
|
||||
return language.GetPhrase("#sf_weather.clear.windy"), "Windy"
|
||||
end
|
||||
return language.GetPhrase("#sf_weather.clear"), "Clear"
|
||||
end
|
||||
else
|
||||
function clear:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
local b_windy = StormFox2.Wind.GetBeaufort(nWind) >= windy
|
||||
if b_windy then
|
||||
return "Windy"
|
||||
end
|
||||
return "Clear"
|
||||
end
|
||||
end
|
||||
-- Icon
|
||||
local m1,m2,m3,m4 = (Material("stormfox2/hud/w_clear.png")),(Material("stormfox2/hud/w_clear_night.png")),(Material("stormfox2/hud/w_clear_windy.png")),(Material("stormfox2/hud/w_clear_cold.png"))
|
||||
function clear.GetSymbol( nTime ) -- What the menu should show
|
||||
return m1
|
||||
end
|
||||
|
||||
function clear.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
local b_day = StormFox2.Time.IsDay(nTime)
|
||||
local b_cold = nTemp < -2
|
||||
local b_windy = StormFox2.Wind.GetBeaufort(nWind) >= windy
|
||||
if b_windy then
|
||||
return m3
|
||||
elseif b_cold then
|
||||
return m4
|
||||
elseif b_day then
|
||||
return m1
|
||||
else
|
||||
return m2
|
||||
end
|
||||
end
|
||||
|
||||
local bCM = string.Explode(" ", StormFox2.Map.GetSetting("fog_color") or "204 255 255")
|
||||
local bC = Color(tonumber(bCM[1]) or 204, tonumber(bCM[2]) or 255, tonumber(bCM[3]) or 255)
|
||||
-- Day
|
||||
clear:SetSunStamp("topColor",Color(91, 127.5, 255), SF_SKY_DAY)
|
||||
-- clear:SetSunStamp("topColor",StormFox2.util.CCTColor(12000),SF_SKY_DAY)
|
||||
-- clear:SetSunStamp("bottomColor",bC, SF_SKY_DAY)
|
||||
clear:SetSunStamp("bottomColor",StormFox2.util.CCTColor(8000),SF_SKY_DAY)
|
||||
clear:SetSunStamp("fadeBias",0.01, SF_SKY_DAY)
|
||||
clear:SetSunStamp("duskColor",Color(255, 255, 255), SF_SKY_DAY)
|
||||
clear:SetSunStamp("duskIntensity",.64, SF_SKY_DAY)
|
||||
clear:SetSunStamp("duskScale",0.29, SF_SKY_DAY)
|
||||
clear:SetSunStamp("sunSize",20, SF_SKY_DAY)
|
||||
clear:SetSunStamp("sunColor",Color(255, 255, 255), SF_SKY_DAY)
|
||||
clear:SetSunStamp("sunFade",1, SF_SKY_DAY)
|
||||
clear:SetSunStamp("starFade",0, SF_SKY_DAY)
|
||||
--clear:SetSunStamp("fogDensity",0.8, SF_SKY_DAY)
|
||||
-- Night
|
||||
clear:SetSunStamp("topColor",Color(0,0,0), SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("bottomColor",Color(0, 1.5, 5.25), SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("fadeBias",0.12, SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("duskColor",Color(9, 9, 0), SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("duskIntensity",0, SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("duskScale",0, SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("sunSize",0, SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("starFade",100, SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("sunColor",Color(255, 255, 255), SF_SKY_NIGHT)
|
||||
clear:SetSunStamp("sunFade",0, SF_SKY_NIGHT)
|
||||
--clear:SetSunStamp("fogDensity",1, SF_SKY_NIGHT)
|
||||
-- Sunset
|
||||
-- Old Color(170, 85, 43)
|
||||
clear:SetSunStamp("topColor",Color(130.5, 106.25, 149), SF_SKY_SUNSET)
|
||||
--clear:SetSunStamp("bottomColor",Color(204, 98, 5), SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("bottomColor",StormFox2.util.CCTColor(2000), SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("fadeBias",1, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("duskColor",Color(248, 103, 30), SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("duskIntensity",1, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("duskScale",0.3, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("sunSize",30, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("sunColor",Color(255, 255, 255), SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("sunFade",.5, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("starFade",30, SF_SKY_SUNSET)
|
||||
--clear:SetSunStamp("fogDensity",0.8, SF_SKY_SUNSET)
|
||||
-- Sunrise
|
||||
clear:SetSunStamp("topColor",Color(130.5, 106.25, 149), SF_SKY_SUNRISE)
|
||||
--clear:SetSunStamp("bottomColor",Color(204, 98, 5), SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("bottomColor",StormFox2.util.CCTColor(2000), SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("fadeBias",0.5, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("duskColor",Color(248, 103, 30), SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("duskIntensity",0.4, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("duskScale",0.6, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("sunSize",20, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("sunColor",Color(255, 255, 255), SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("sunFade",.5, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("starFade",30, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("fogDensity",0.8, SF_SKY_SUNRISE)
|
||||
-- Cevil
|
||||
clear:CopySunStamp( SF_SKY_NIGHT, SF_SKY_CEVIL ) -- Copy the night sky
|
||||
clear:SetSunStamp("fadeBias",0.01, SF_SKY_CEVIL)
|
||||
clear:SetSunStamp("sunSize",0, SF_SKY_CEVIL)
|
||||
clear:SetSunStamp("bottomColor",StormFox2.util.CCTColor(0), SF_SKY_CEVIL)
|
||||
|
||||
-- Default variables. These don't change.
|
||||
clear:Set("moonColor", Color( 205, 205, 205 ))
|
||||
local moonSize = StormFox2.Setting.GetObject("moonsize")
|
||||
clear:Set("moonSize",moonSize:GetValue())
|
||||
|
||||
moonSize:AddCallback(function(var)
|
||||
clear:Set("moonSize",moonSize:GetValue())
|
||||
end, "SF_moonSizeUpdate")
|
||||
clear:Set("moonTexture", "stormfox2/effects/moon/moon.png" )
|
||||
clear:Set("skyVisibility",100) -- Blocks out the sun/moon
|
||||
clear:Set("mapDayLight",100)
|
||||
clear:Set("mapNightLight",0)
|
||||
clear:Set("clouds",0)
|
||||
clear:Set("HDRScale",0.7)
|
||||
|
||||
clear:Set("fogDistance", 400000)
|
||||
clear:Set("fogIndoorDistance", 400000)
|
||||
--clear:Set("fogEnd",90000)
|
||||
--clear:Set("fogStart",0)
|
||||
|
||||
-- Static values
|
||||
clear:Set("starSpeed", 0.001)
|
||||
clear:Set("starScale", 2.2)
|
||||
clear:Set("starTexture", "skybox/starfield")
|
||||
clear:Set("enableThunder") -- Tells the generator that this weather_type can't have thunder.
|
||||
|
||||
-- 2D skyboxes
|
||||
if SERVER then
|
||||
local t_day, t_night, t_sunrise, t_sunset
|
||||
t_day = {"sky_day01_05", "sky_day01_04", "sky_day02_01","sky_day02_03","sky_day02_04","sky_day02_05"}
|
||||
t_sunrise = {"sky_day01_05", "sky_day01_06", "sky_day01_08"}
|
||||
t_sunset = {"sky_day02_02", "sky_day02_01"}
|
||||
t_night = {"sky_day01_09"}
|
||||
|
||||
clear:SetSunStamp("skyBox",t_day, SF_SKY_DAY)
|
||||
clear:SetSunStamp("skyBox",t_sunrise, SF_SKY_SUNRISE)
|
||||
clear:SetSunStamp("skyBox",t_sunset, SF_SKY_SUNSET)
|
||||
clear:SetSunStamp("skyBox",t_night, SF_SKY_NIGHT)
|
||||
end
|
||||
652
lua/stormfox2/weathers/cloudrain.lua
Normal file
652
lua/stormfox2/weathers/cloudrain.lua
Normal file
@@ -0,0 +1,652 @@
|
||||
--[[
|
||||
| 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 clamp,max,min,random = math.Clamp,math.max,math.min,math.random
|
||||
-- Rain and cloud is nearly the same.
|
||||
local cloudy = StormFox2.Weather.Add( "Cloud" )
|
||||
local rain = StormFox2.Weather.Add( "Rain", "Cloud" )
|
||||
-- cloudy.spawnable = true Cloud is not spawnable. Since it is a "default" when it is cloudy
|
||||
rain.spawnable = true
|
||||
rain.thunder = function(percent) -- The amount of strikes pr minute
|
||||
return percent > 0.5 and random(10) > 5 and percent * 3 or 0
|
||||
end
|
||||
-- Cloud icon
|
||||
do
|
||||
-- Description
|
||||
if CLIENT then
|
||||
function cloudy:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if StormFox2.Wind.GetBeaufort(nWind) >= 10 then
|
||||
return language.GetPhrase('sf_weather.cloud.storm')
|
||||
elseif bThunder then
|
||||
return language.GetPhrase('sf_weather.cloud.thunder')
|
||||
else
|
||||
return language.GetPhrase('sf_weather.cloud')
|
||||
end
|
||||
end
|
||||
else
|
||||
function cloudy:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if StormFox2.Wind.GetBeaufort(nWind) >= 10 then
|
||||
return "Storm"
|
||||
elseif bThunder then
|
||||
return "Thunder"
|
||||
else
|
||||
return "Cloudy"
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Icon
|
||||
local m_def = Material("stormfox2/hud/w_cloudy.png")
|
||||
local m_night = Material("stormfox2/hud/w_cloudy_night.png")
|
||||
local m_windy = Material("stormfox2/hud/w_cloudy_windy.png")
|
||||
local m_thunder = Material("stormfox2/hud/w_cloudy_thunder.png")
|
||||
function cloudy.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function cloudy.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
local b_day = StormFox2.Time.IsDay(nTime)
|
||||
local b_cold = nTemp < -2
|
||||
local b_windy = StormFox2.Wind.GetBeaufort(nWind) >= 7
|
||||
local b_H = nFraction > 0.5
|
||||
if bThunder then
|
||||
return m_thunder
|
||||
elseif b_windy then
|
||||
return m_windy
|
||||
elseif b_H or b_day then
|
||||
return m_def
|
||||
else
|
||||
return m_night
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Rain icon
|
||||
do
|
||||
-- Description
|
||||
if CLIENT then
|
||||
function rain:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if StormFox2.Wind.GetBeaufort(nWind) >= 10 then
|
||||
return language.GetPhrase('sf_weather.cloud.storm'), 'Storm'
|
||||
elseif bThunder then
|
||||
return language.GetPhrase('sf_weather.cloud.thunder'), 'Thunder'
|
||||
elseif nTemp > 0 then
|
||||
return language.GetPhrase('sf_weather.rain'), 'Raining'
|
||||
elseif nTemp > -2 then
|
||||
return language.GetPhrase('sf_weather.rain.sleet'), 'Sleet'
|
||||
else
|
||||
return language.GetPhrase('sf_weather.rain.snow'), 'Snowing'
|
||||
end
|
||||
end
|
||||
else
|
||||
function rain:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if StormFox2.Wind.GetBeaufort(nWind) >= 10 then
|
||||
return 'Storm', 'Storm'
|
||||
elseif bThunder then
|
||||
return 'Thunder', 'Thunder'
|
||||
elseif nTemp > 0 then
|
||||
return 'Raining', 'Raining'
|
||||
elseif nTemp > -2 then
|
||||
return 'Sleet', 'Sleet'
|
||||
else
|
||||
return 'Snowing', 'Snowing'
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Icon
|
||||
local m_def = Material("stormfox2/hud/w_raining.png")
|
||||
local m_def_light = Material("stormfox2/hud/w_raining_light.png")
|
||||
local m_thunder = Material("stormfox2/hud/w_raining_thunder.png")
|
||||
local m_windy = Material("stormfox2/hud/w_raining_windy.png")
|
||||
local m_snow = Material("stormfox2/hud/w_snowing.png")
|
||||
local m_sleet = Material("stormfox2/hud/w_sleet.png")
|
||||
function rain.GetSymbol( nTime, nTemp ) -- What the menu should show
|
||||
if nTemp < -4 then
|
||||
return m_snow
|
||||
end
|
||||
return m_def
|
||||
end
|
||||
function rain.GetIcon( _, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
local b_windy = StormFox2.Wind.GetBeaufort(nWind) >= 7
|
||||
if bThunder then
|
||||
return m_thunder
|
||||
elseif b_windy and nTemp > -4 then
|
||||
return m_windy
|
||||
elseif nTemp > 0 then
|
||||
if nFraction > 0.4 then
|
||||
return m_def
|
||||
else
|
||||
return m_def_light
|
||||
end
|
||||
elseif nTemp <= -4 then
|
||||
return m_snow
|
||||
else
|
||||
return m_sleet
|
||||
end
|
||||
end
|
||||
function rain.LogicRelay()
|
||||
if StormFox2.Temperature.Get() < -1 then
|
||||
return "snow"
|
||||
end
|
||||
return "rain"
|
||||
end
|
||||
end
|
||||
|
||||
-- Sky and default weather variables
|
||||
do
|
||||
-- Day --
|
||||
cloudy:SetSunStamp("topColor",Color(3.0, 2.9, 3.5), SF_SKY_DAY)
|
||||
cloudy:SetSunStamp("bottomColor",Color(20, 25, 25), SF_SKY_DAY)
|
||||
cloudy:SetSunStamp("duskColor",Color(3, 2.9, 3.5), SF_SKY_DAY)
|
||||
cloudy:SetSunStamp("duskScale",1, SF_SKY_DAY)
|
||||
cloudy:SetSunStamp("HDRScale",0.33, SF_SKY_DAY)
|
||||
-- Night
|
||||
cloudy:SetSunStamp("topColor",Color(0.4, 0.2, 0.54), SF_SKY_NIGHT)
|
||||
cloudy:SetSunStamp("bottomColor",Color(2.25, 2.25,2.25),SF_SKY_NIGHT)
|
||||
--cloudy:SetSunStamp("bottomColor",Color(14.3* 0.5,14.8* 0.5,15.2* 0.5), SF_SKY_NIGHT)
|
||||
cloudy:SetSunStamp("duskColor",Color(.4, .2, .54), SF_SKY_NIGHT)
|
||||
cloudy:SetSunStamp("duskScale",0, SF_SKY_NIGHT)
|
||||
cloudy:SetSunStamp("HDRScale",0.1, SF_SKY_NIGHT)
|
||||
-- Sunset/rise
|
||||
cloudy:SetSunStamp("duskScale",0.26, SF_SKY_SUNSET)
|
||||
cloudy:SetSunStamp("duskScale",0.26, SF_SKY_SUNRISE)
|
||||
|
||||
cloudy:Set("starFade",0)
|
||||
cloudy:Set("mapDayLight",10)
|
||||
cloudy:Set("skyVisibility",0)
|
||||
cloudy:Set("clouds",1)
|
||||
cloudy:Set("enableThunder", true)
|
||||
|
||||
rain:Set("mapDayLight",0)
|
||||
local cDay, cNight = Color(20, 25, 25), Color(2.25, 2.25,2.25)
|
||||
local n = 7
|
||||
local cDayM, cNightM = Color(40 * 2, 50 * 2, 50 * 2), Color(2.25 * n, 2.25 * n,2.25 * n)
|
||||
rain:Set("bottomColor",function(nStamp)
|
||||
local temp = StormFox2.Temperature.Get()
|
||||
if temp >= 0 then
|
||||
return nStamp == SF_SKY_DAY and cDay or cNight
|
||||
elseif temp <= -4 then
|
||||
return nStamp == SF_SKY_DAY and cDayM or cNightM
|
||||
end
|
||||
local cRain = nStamp == SF_SKY_DAY and cDay or cNight
|
||||
local cSnow = nStamp == SF_SKY_DAY and cDayM or cNightM
|
||||
return StormFox2.Mixer.Blender(temp / 4 + 1, cSnow, cRain)
|
||||
end)
|
||||
--rain:SetSunStamp("fogEnd",800,SF_SKY_DAY)
|
||||
--rain:SetSunStamp("fogEnd",800,SF_SKY_SUNRISE)
|
||||
--rain:SetSunStamp("fogEnd",2000,SF_SKY_NIGHT)
|
||||
--rain:SetSunStamp("fogEnd",2000,SF_SKY_BLUE_HOUR)
|
||||
--rain:Set("fogDensity",1)
|
||||
--rain:Set("fogStart",0)
|
||||
rain:Set("fogDistance", function()
|
||||
local wF = StormFox2.Wind.GetForce()
|
||||
local temp = clamp(StormFox2.Temperature.Get() / 4 + 1,0,1)
|
||||
if wF <= 0 then return 6000 end
|
||||
local tempDist = 2000 + temp * 5200
|
||||
local multi = max(0, 26 - temp * 8)
|
||||
return max(tempDist - multi * wF,0)
|
||||
end)
|
||||
rain:Set("fogIndoorDistance", 5500)
|
||||
-- rain:SetSunStamp("fogDistance",2000, SF_SKY_DAY)
|
||||
-- rain:SetSunStamp("fogDistance",2500, SF_SKY_SUNSET)
|
||||
-- rain:SetSunStamp("fogDistance",2000, SF_SKY_NIGHT)
|
||||
-- rain:SetSunStamp("fogDistance",2500, SF_SKY_SUNRISE)
|
||||
|
||||
end
|
||||
-- Window render
|
||||
local rain_normal_material = Material("stormfox2/effects/window/rain_normal")
|
||||
local rain_t = StormFox2.Terrain.Create("rain")
|
||||
do
|
||||
local raindrops = {}
|
||||
local raindrops_mat = {(Material("stormfox2/effects/window/raindrop_normal")),(Material("stormfox2/effects/window/raindrop_normal2")),(Material("stormfox2/effects/window/raindrop_normal3"))}
|
||||
local s = 2
|
||||
rain:RenderWindowRefract64x64(function(w, h)
|
||||
if StormFox2.Temperature.Get() < -1 then return false end
|
||||
local QT = StormFox2.Client.GetQualityNumber()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
-- Base
|
||||
surface.SetDrawColor(Color(255,255,255,255 * P))
|
||||
surface.SetMaterial(rain_normal_material)
|
||||
local c = (-SysTime() / 1000) % 1
|
||||
surface.DrawTexturedRectUV(0,0, w, h, 0, c, s, c + s )
|
||||
-- Create raindrop
|
||||
if #raindrops < math.Clamp(QT * 10, 5 ,65 * P) and random(100) <= 90 then
|
||||
local s = random(6,10)
|
||||
local x,y = random(s, w - s * 2), random(s, h * 0.8)
|
||||
local sp = random(10, 50)
|
||||
local lif = CurTime() + random(3,5)
|
||||
local m = table.Random(raindrops_mat)
|
||||
table.insert(raindrops, {x,y,s,m,sp,lif})
|
||||
end
|
||||
-- Render raindrop
|
||||
local r = {}
|
||||
for i,v in ipairs(raindrops) do
|
||||
local lif = (v[6] - CurTime()) * 10
|
||||
local a_n = h - v[2] - v[3]
|
||||
local a = min(25.5,min(a_n,lif)) * 10
|
||||
if a > 0 then
|
||||
surface.SetMaterial(v[4])
|
||||
surface.SetDrawColor(Color(255,255,255,a))
|
||||
surface.DrawTexturedRect(v[1],v[2],v[3],v[3])
|
||||
v[2] = v[2] + FrameTime() * v[5]
|
||||
else
|
||||
table.insert(r, i)
|
||||
end
|
||||
end
|
||||
-- Remove raindrop
|
||||
for i = #r,1,-1 do
|
||||
table.remove(raindrops, r[i])
|
||||
end
|
||||
end)
|
||||
|
||||
-- Snow window
|
||||
local mat = Material("stormfox2/effects/window/snow")
|
||||
local mat2 = Material("stormfox2/effects/blizzard.png","noclamp")
|
||||
local mat3 = Material("stormfox2/effects/rainstorm.png","noclamp")
|
||||
local size = 0.5
|
||||
local function RenderWindow(w, h)
|
||||
if StormFox2.Temperature.Get() > -2 then
|
||||
local wi = StormFox2.Wind.GetForce()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local lum = max(min(25 + StormFox2.Weather.GetLuminance(), 255),150)
|
||||
if P * wi < 10 then return false end
|
||||
-- Storm
|
||||
local c = Color(lum,lum,lum,math.min(255, wi * 3))
|
||||
surface.SetDrawColor(c)
|
||||
surface.SetMaterial(mat3)
|
||||
for i = 1, math.max(1, wi / 20) do
|
||||
local cx = CurTime() * -1 % size
|
||||
local cu = (CurTime() * -(4 + i)) % size
|
||||
local fx = i / 3 + cx
|
||||
surface.DrawTexturedRectUV(0,0,w,h, fx, cu, fx + size, size + cu)
|
||||
end
|
||||
else
|
||||
local P = 1 - StormFox2.Weather.GetPercent()
|
||||
local wi = StormFox2.Wind.GetForce()
|
||||
local lum = max(min(25 + StormFox2.Weather.GetLuminance(), 255),150)
|
||||
local c = Color(lum,lum,lum)
|
||||
local oSF = StormFox2.Environment.GetOutSideFade()
|
||||
if wi > 5 and oSF < 1 then
|
||||
c.a = 255 - (oSF * 255)
|
||||
surface.SetDrawColor(c)
|
||||
surface.SetMaterial(mat2)
|
||||
local cu = CurTime() * 3
|
||||
for i = 1, wi / 20 do
|
||||
local sz = (i * 3.333) % 3
|
||||
local sx = i * 3 + (cu * 0.2) % sz
|
||||
local sy = i * 5 + -cu % (sz * 0.5)
|
||||
surface.DrawTexturedRectUV(0,0,w,h,sx,sy,sx + sz,sy + sz)
|
||||
end
|
||||
c.a = 255
|
||||
end
|
||||
surface.SetMaterial(mat)
|
||||
surface.SetDrawColor(c)
|
||||
surface.DrawTexturedRect(0,h * 0.12 * P,w,h)
|
||||
end
|
||||
end
|
||||
rain:RenderWindow( RenderWindow )
|
||||
end
|
||||
-- Snow Terrain and footsteps
|
||||
do
|
||||
local snow = StormFox2.Terrain.Create("snow")
|
||||
|
||||
-- Make the snow terrain apply, if temp is low
|
||||
rain:SetTerrain( function()
|
||||
if SERVER then
|
||||
StormFox2.Map.w_CallLogicRelay(rain.LogicRelay())
|
||||
end
|
||||
return (StormFox2.Data.GetFinal("Temp") or 0) < -3 and snow or rain_t
|
||||
end)
|
||||
|
||||
-- Make the snow stay, until temp is high or it being replaced.
|
||||
snow:LockUntil(function()
|
||||
return StormFox2.Temperature.Get() > -2
|
||||
end)
|
||||
|
||||
-- Footprints
|
||||
snow:MakeFootprints(true,{
|
||||
"stormfox2/footstep/footstep_snow0.mp3",
|
||||
"stormfox2/footstep/footstep_snow1.mp3",
|
||||
"stormfox2/footstep/footstep_snow2.mp3",
|
||||
"stormfox2/footstep/footstep_snow3.mp3",
|
||||
"stormfox2/footstep/footstep_snow4.mp3",
|
||||
"stormfox2/footstep/footstep_snow5.mp3",
|
||||
"stormfox2/footstep/footstep_snow6.mp3",
|
||||
"stormfox2/footstep/footstep_snow7.mp3",
|
||||
"stormfox2/footstep/footstep_snow8.mp3",
|
||||
"stormfox2/footstep/footstep_snow9.mp3"
|
||||
},"snow.step")
|
||||
|
||||
snow:SetGroundTexture("nature/snowfloor001a")
|
||||
snow:AddTextureSwap("models/buggy/buggy001","stormfox2/textures/buggy001-snow")
|
||||
snow:AddTextureSwap("models/vehicle/musclecar_col","stormfox2/textures/musclecar_col-snow")
|
||||
|
||||
-- Other snow textures
|
||||
-- DOD
|
||||
if IsMounted("dod") then
|
||||
snow:AddTextureSwap("models/props_foliage/hedge_128", "models/props_foliage/hedgesnow_128")
|
||||
snow:AddTextureSwap("models/props_fortifications/hedgehog", "models/props_fortifications/hedgehog_snow")
|
||||
snow:AddTextureSwap("models/props_fortifications/sandbags", "models/props_fortifications/sandbags_snow")
|
||||
snow:AddTextureSwap("models/props_fortifications/dragonsteeth", "models/props_fortifications/dragonsteeth_snow")
|
||||
snow:AddTextureSwap("models/props_normandy/logpile", "models/props_normandy/logpile_snow")
|
||||
snow:AddTextureSwap("models/props_urban/light_fixture01", "models/props_urban/light_fixture01_snow")
|
||||
snow:AddTextureSwap("models/props_urban/light_streetlight01", "models/props_urban/light_streetlight01_snow")
|
||||
snow:AddTextureSwap("models/props_urban/light_fixture01_on", "models/props_urban/light_fixture01_snow_on")
|
||||
snow:AddTextureSwap("models/props_urban/light_streetlight01_on", "models/props_urban/light_streetlight01_snow_on")
|
||||
end
|
||||
-- TF2
|
||||
if IsMounted("tf") then
|
||||
snow:AddTextureSwap("models/props_foliage/shrub_03","models/props_foliage/shrub_03_snow")
|
||||
snow:AddTextureSwap("models/props_swamp/shrub_03","models/props_foliage/shrub_03_snow")
|
||||
snow:AddTextureSwap("models/props_foliage/shrub_03_skin2","models/props_foliage/shrub_03_snow")
|
||||
|
||||
snow:AddTextureSwap("models/props_foliage/grass_02","models/props_foliage/grass_02_snow")
|
||||
snow:AddTextureSwap("models/props_foliage/grass_02_dark","models/props_foliage/grass_02_snow")
|
||||
snow:AddTextureSwap("nature/blendgrassground001","nature/blendgrasstosnow001")
|
||||
snow:AddTextureSwap("nature/blendgrassground002","nature/blendgrasstosnow001")
|
||||
snow:AddTextureSwap("nature/blendgrassground007","nature/blendgrasstosnow001")
|
||||
snow:AddTextureSwap("detail/detailsprites_2fort","detail/detailsprites_viaduct_event")
|
||||
snow:AddTextureSwap("detail/detailsprites_dustbowl","detail/detailsprites_viaduct_event")
|
||||
snow:AddTextureSwap("detail/detailsprites_trainyard","detail/detailsprites_viaduct_event")
|
||||
snow:AddTextureSwap("models/props_farm/tree_leaves001","models/props_farm/tree_branches001")
|
||||
snow:AddTextureSwap("models/props_foliage/tree_pine01","models/props_foliage/tree_pine01_snow")
|
||||
for _,v in ipairs({"02","05","06","09","10","10a"}) do
|
||||
snow:AddTextureSwap("models/props_forest/cliff_wall_" .. v,"models/props_forest/cliff_wall_" .. v .. "_snow")
|
||||
end
|
||||
snow:AddTextureSwap("models/props_island/island_tree_leaves02","models/props_island/island_tree_roots01")
|
||||
snow:AddTextureSwap("models/props_forest/train_stop","models/props_forest/train_stop_snow")
|
||||
end
|
||||
end
|
||||
|
||||
-- Rain particles and sound
|
||||
if CLIENT then
|
||||
-- Sound
|
||||
local rain_light = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_light.ogg", SF_AMB_OUTSIDE, 1 )
|
||||
local rain_window = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_glass.ogg", SF_AMB_WINDOW, 0.1 )
|
||||
local rain_outside = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_outside.ogg", SF_AMB_NEAR_OUTSIDE, 0.1 )
|
||||
--local rain_underwater = StormFox2.Ambience.CreateAmbienceSnd( "", SF_AMB_UNDER_WATER, 0.1 ) Unused
|
||||
local rain_watersurf = StormFox2.Ambience.CreateAmbienceSnd( "ambient/water/water_run1.wav", SF_AMB_UNDER_WATER_Z, 0.1 )
|
||||
local rain_roof_wood = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_roof.ogg", SF_AMB_ROOF_WOOD, 0.1 )
|
||||
local rain_roof_metal = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_roof_metal.ogg", SF_AMB_ROOF_METAL, 0.1 )
|
||||
local rain_glass = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_glass.ogg", SF_AMB_ROOF_GLASS, 0.1 )
|
||||
rain:AddAmbience( rain_light )
|
||||
rain:AddAmbience( rain_window )
|
||||
rain:AddAmbience( rain_outside )
|
||||
rain:AddAmbience( rain_watersurf )
|
||||
rain:AddAmbience( rain_roof_wood )
|
||||
rain:AddAmbience( rain_roof_metal )
|
||||
rain:AddAmbience( rain_glass )
|
||||
-- Edit watersurf
|
||||
rain_watersurf:SetFadeDistance(0,100)
|
||||
rain_watersurf:SetVolume( 0.05 )
|
||||
rain_watersurf:SetPlaybackRate(2)
|
||||
-- Edit rain_glass
|
||||
rain_roof_metal:SetFadeDistance(10,400)
|
||||
rain_glass:SetFadeDistance(10, 400)
|
||||
rain_window:SetFadeDistance(100, 200)
|
||||
-- Edit rain_outside
|
||||
rain_outside:SetFadeDistance(100, 200)
|
||||
-- Materials
|
||||
local m_rain = Material("stormfox2/raindrop.png")
|
||||
local m_rain2 = Material("stormfox2/effects/raindrop-multi2.png")
|
||||
local m_rain3 = Material("stormfox2/effects/raindrop-multi3.png")
|
||||
local m_rain_multi = Material("stormfox2/effects/snow-multi.png","noclamp smooth")
|
||||
local m_snow = Material("particle/snow")
|
||||
local m_snow1 = Material("stormfox2/effects/snowflake1.png")
|
||||
local m_snow2 = Material("stormfox2/effects/snowflake2.png")
|
||||
local m_snow3 = Material("stormfox2/effects/snowflake3.png")
|
||||
local t_snow = {m_snow1, m_snow2, m_snow3}
|
||||
local m_snowmulti = Material("stormfox2/effects/snow-multi.png")
|
||||
local m_snowmulti2 = Material("stormfox2/effects/snow-multi2.png")
|
||||
|
||||
|
||||
-- Make the distant rain start higer up.
|
||||
|
||||
-- Update the rain templates every 10th second
|
||||
function rain.TickSlow()
|
||||
local W = StormFox2.Wind.GetForce()
|
||||
local P = StormFox2.Weather.GetPercent() * (0.5 + W / 30)
|
||||
local L = StormFox2.Weather.GetLuminance()
|
||||
local T = StormFox2.Temperature.Get() + 2
|
||||
local TL = StormFox2.Thunder.GetLight()
|
||||
|
||||
local TP = math.Clamp(T / 4,0,1)
|
||||
|
||||
rain_outside:SetVolume( P * TP )
|
||||
rain_light:SetVolume( P * TP )
|
||||
rain_window:SetVolume( P * 0.3 * TP )
|
||||
rain_roof_wood:SetVolume( P * 0.3 * TP )
|
||||
rain_roof_metal:SetVolume( P * 1 * TP )
|
||||
rain_glass:SetVolume( P * 0.5 * TP )
|
||||
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local speed = 0.72 + 0.36 * P
|
||||
StormFox2.Misc.rain_template:SetSpeed( speed )
|
||||
StormFox2.Misc.rain_template_medium:SetSpeed( speed )
|
||||
StormFox2.Misc.rain_template_medium:SetAlpha( L / 5)
|
||||
end
|
||||
-- Gets called every tick to add rain.
|
||||
local multi_dis = 1200
|
||||
local m2 = Material("particle/particle_smokegrenade1")
|
||||
local tc = Color(150,150,150)
|
||||
local snow_col = Color(255,255,255)
|
||||
function rain.Think()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local L = StormFox2.Weather.GetLuminance()
|
||||
local W = StormFox2.Wind.GetForce()
|
||||
if StormFox2.DownFall.GetGravity() < 0 then return end -- Rain can't come from the ground.
|
||||
local T = StormFox2.Temperature.Get() + 2
|
||||
if T > 0 or T > random(-3, 0) then -- Spawn rain particles
|
||||
-- Set alpha
|
||||
local s = 1.22 + 1.56 * P
|
||||
StormFox2.Misc.rain_template:SetSize( s , 5.22 + 7.56 * P)
|
||||
StormFox2.Misc.rain_template:SetColor(tc)
|
||||
StormFox2.Misc.rain_template:SetAlpha(min(100 + 15 * P + L,255))
|
||||
StormFox2.Misc.rain_template_medium:SetAlpha(min(150 + 15 * P + L,255) /3)
|
||||
StormFox2.Misc.rain_template_fog:SetAlpha( L )
|
||||
local rain_distance = min(random(300,900), StormFox2.Fog.GetEnd())
|
||||
-- Spawn rain particles
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template, 10, 700, 10 + P * 900, 5, vNorm ) or {} ) do
|
||||
v:SetSize( 1.22 + 1.56 * P * math.Rand(1,2), 5.22 + 7.56 * P )
|
||||
end
|
||||
-- Spawn distant rain
|
||||
if P > 0.15 then
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_medium, 250, 1500, 10 + P * 300, 250, vNorm ) or {} ) do
|
||||
local d = v:GetDistance()
|
||||
if d < 700 then
|
||||
if random()>0.5 then
|
||||
v:SetMaterial( m_rain2 )
|
||||
else
|
||||
v:SetMaterial(m_rain3 )
|
||||
end
|
||||
v:SetSize( 250, 250 )
|
||||
v:SetSpeed( v:GetSpeed() * math.Rand(1,2))
|
||||
else
|
||||
v:SetSize( 1.22 + 1.56 * P * math.Rand(1,3) * 10, 5.22 + 7.56 * P * 10 )
|
||||
end
|
||||
v:SetAlpha(min(15 + 4 * P + L,255) * 0.2)
|
||||
end
|
||||
end
|
||||
if P > (0.7 - W * 0.4) and L > 5 then -- If it is too dark, make it invis
|
||||
local max_fog = (90 + P * (20 + (W / 80) * 102)) * 0.5
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_fog, rain_distance, rain_distance * 2 , max_fog, 200, vNorm ) or {} ) do
|
||||
local d = v:GetDistance()
|
||||
if not d or d < 500 then
|
||||
v:SetSize( 225, 500 )
|
||||
else
|
||||
v:SetSize( d * .45, d)
|
||||
end
|
||||
if random(0,1) >= 0.5 then
|
||||
v:SetMaterial(m2)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Spawn snow particles
|
||||
local force_multi = max(1, (W / 6))
|
||||
local snow_distance = min(random(140,500), StormFox2.Fog.GetEnd())
|
||||
local d = min(snow_distance / 500, 1)
|
||||
local snow_size = math.Rand(3,5) * d * max(0.7,P)
|
||||
local s = math.Rand(3,5) * d * max(0.7,P)
|
||||
local snow_speed = 0.15 * force_multi
|
||||
|
||||
StormFox2.Misc.snow_template:SetSpeed( snow_speed )
|
||||
local n = max(min(L * 3, 255), 150)
|
||||
snow_col.r = n
|
||||
snow_col.g = n
|
||||
snow_col.b = n
|
||||
StormFox2.Misc.snow_template:SetColor(snow_col)
|
||||
StormFox2.Misc.snow_template_multi:SetColor(snow_col)
|
||||
local max_normal = 40 * P * (50 - W)
|
||||
if StormFox2.Environment.GetOutSideFade() < 0.9 then
|
||||
max_normal = 40 * P
|
||||
end
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.snow_template, 20, snow_distance, max_normal, 5, vNorm ) or {} ) do
|
||||
v:SetSize( s, s )
|
||||
v:SetSpeed( math.Rand(1, 2) * snow_speed)
|
||||
if snow_speed > 0.15 and random(snow_speed)> 0.15 then
|
||||
v.hitType = SF_DOWNFALL_HIT_NIL
|
||||
end
|
||||
end
|
||||
-- Spawn snow distant
|
||||
if P > 0.15 then
|
||||
local max_multi = 10 * (P - 0.15) * (70 - W)
|
||||
if StormFox2.Environment.GetOutSideFade() < 0.9 then
|
||||
max_normal = 10 * (P - 0.15)
|
||||
end
|
||||
local snow_distance = min(random(300,900), StormFox2.Fog.GetEnd())
|
||||
local d = max(snow_distance / 900, 0.5)
|
||||
local snow_size = math.Rand(0.5,1) * max(0.7,P) * 500 * d
|
||||
local snow_speed = 0.15 * d * force_multi
|
||||
StormFox2.Misc.snow_template:SetSpeed( mult_speed )
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.snow_template_multi, 500, snow_distance / 1, max_multi, s, vNorm ) or {} ) do
|
||||
v:SetSize( snow_size, snow_size )
|
||||
v:SetSpeed( math.Rand(1, 2) * snow_speed)
|
||||
v:SetRoll( math.Rand(0, 360))
|
||||
if random(0,1) == 0 then
|
||||
v:SetMaterial(m_snowmulti2)
|
||||
end
|
||||
end
|
||||
end
|
||||
local max_fog = (90 + P * (20 + (W / 80) * 102))
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_fog, snow_distance, snow_distance * 2 , max_fog, 200, vNorm ) or {} ) do
|
||||
local d = v:GetDistance()
|
||||
if not d or d < 500 then
|
||||
v:SetSize( 225, 500 )
|
||||
else
|
||||
v:SetSize( d * .45, d)
|
||||
end
|
||||
if random(0,1) >= 0 then
|
||||
v:SetMaterial(m2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Render Filter (Screen filter, this is additive)
|
||||
local blizard = Material("stormfox2/effects/blizzard.png", "noclamp")
|
||||
local storm = Material("stormfox2/effects/rainstorm.png", "noclamp")
|
||||
local sx,sy = 0,0
|
||||
local rx,ry,rx2,ry2 = 0,0,0,0
|
||||
--surface.SetDrawColor( Color(255,255,255,34 * a) )
|
||||
--surface.SetMaterial( snowMulti )
|
||||
--surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH() ,c2,0 + c,2 + c2,2 + c)
|
||||
--surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH() ,-c2 + c4,0 + c3,1 - c2 + c4,1 + c3)
|
||||
local up = Vector(0,0,1)
|
||||
|
||||
local function setMaterialRoll(mat, roll, u, v)
|
||||
local matrix = Matrix()
|
||||
local w = mat:Width()
|
||||
local h = mat:Height()
|
||||
matrix:SetAngles(Angle(0,roll,0))
|
||||
matrix:Translate(Vector(u, v, 0))
|
||||
mat:SetMatrix("$basetexturetransform", matrix)
|
||||
end
|
||||
function rain.DepthFilter(w, h, a)
|
||||
a = (a - 0.50) * 2
|
||||
if a <= 0 then return end
|
||||
local windDir = (-StormFox2.Wind.GetNorm()):Angle()
|
||||
local rainscale = (StormFox2.Temperature.Get() + 2) / 2
|
||||
|
||||
local ad = math.AngleDifference(StormFox2.Wind.GetYaw() + 180, StormFox2.util.GetCalcView().ang.y)
|
||||
local ada = math.sin(math.rad(ad))
|
||||
-- 0 = directly into the wind
|
||||
-- 1 = directly to the side of the wind
|
||||
|
||||
-- 0 = not moving at all
|
||||
-- 1 = max movment
|
||||
local A = EyeAngles():Forward()
|
||||
local B = windDir:Forward()
|
||||
local D = math.abs(A:Dot(B))
|
||||
local C = 1 - D
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local W = math.min(1, StormFox2.Wind.GetForce() / 60)
|
||||
|
||||
local B2 = windDir:Right()
|
||||
local D2 = (A:Dot(B2))
|
||||
|
||||
if rainscale > -1 then
|
||||
local WP = math.min(1, P) -- 0 - 1 Wimdy
|
||||
local wind_x = ada * -C * 4 * WP
|
||||
local wind_y = -8 * math.max(0.5, WP)
|
||||
local roll = (windDir.p - 270) * -D2 * 0.8
|
||||
rx = (rx + FrameTime() * wind_x) % 1
|
||||
ry = (ry + FrameTime() * wind_y) % 1
|
||||
setMaterialRoll(storm, 180 - roll + 3, rx, ry)
|
||||
surface.SetMaterial( storm )
|
||||
surface.SetDrawColor( Color(255,255,255,154 * a * math.max(0.1, W) * WP * math.max(C,0)) )
|
||||
local s,s2 = 1.7, 1.8
|
||||
surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), 0, 0,0 + s, 0 + s)
|
||||
-- surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), rx,ry, rx + s2,ry + s2)
|
||||
elseif rainscale < 1 then
|
||||
local WP = math.min(1, W)
|
||||
local wind_x = ada * -C * 4 * WP
|
||||
local wind_y = -8 * math.max(0.5, WP)
|
||||
local roll = (windDir.p - 270) * -ada
|
||||
sx = (sx + FrameTime() * wind_x) % 1
|
||||
sy = (sy + FrameTime() * wind_y) % 1
|
||||
setMaterialRoll(blizard, 180 - roll + 14, w / 2, h / 2)
|
||||
surface.SetDrawColor( Color(255,255,255,144 * a * math.max(WP,0.1) * ((P) * 1.15) ) )
|
||||
surface.SetMaterial( blizard )
|
||||
surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), sx, sy, 2 + sx, 2 + sy)
|
||||
surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), -sx, sy, 1 - sx, 1 + sy)
|
||||
|
||||
|
||||
-- surface.SetDrawColor( Color(255,255,255,255 * a * WP * D ) )
|
||||
-- surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), 0, 0, 1, 1)
|
||||
-- surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), 0, 0, 0.6, 0.6)
|
||||
end
|
||||
--print(">",w,h,a)
|
||||
end
|
||||
|
||||
-- Render water
|
||||
local debri = Material("stormfox2/effects/terrain/snow_water")
|
||||
rain.PreDrawTranslucentRenderables = function( a, b)
|
||||
local f = 5 + StormFox2.Temperature.Get()
|
||||
if f > 0 then return end
|
||||
debri:SetFloat("$alpha",StormFox2.Weather.GetPercent() * 0.3 * math.Clamp(-f, 0, 1))
|
||||
render.SetMaterial(debri)
|
||||
StormFox2.Environment.DrawWaterOverlay( b )
|
||||
end
|
||||
end
|
||||
|
||||
-- 2D skyboxes
|
||||
if SERVER then
|
||||
local t_day, t_night, t_sunrise, t_sunset
|
||||
t_day = {"sky_day03_02", "sky_day03_03", "sky_day03_04"}
|
||||
t_sunrise = {"sky_day01_01"}
|
||||
t_sunset = {"sky_day01_06"}
|
||||
t_night = {"sky_day01_09"}
|
||||
|
||||
cloudy:SetSunStamp("skyBox",t_day, SF_SKY_DAY)
|
||||
cloudy:SetSunStamp("skyBox",t_sunrise, SF_SKY_SUNRISE)
|
||||
cloudy:SetSunStamp("skyBox",t_sunset, SF_SKY_SUNSET)
|
||||
cloudy:SetSunStamp("skyBox",t_night, SF_SKY_NIGHT)
|
||||
end
|
||||
199
lua/stormfox2/weathers/fallout.lua
Normal file
199
lua/stormfox2/weathers/fallout.lua
Normal file
@@ -0,0 +1,199 @@
|
||||
--[[
|
||||
| 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 rad = StormFox2.Weather.Add( "Radioactive", "Rain" )
|
||||
if CLIENT then
|
||||
function rad:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return language.GetPhrase('sf_weather.fallout'), "Nuclear fallout"
|
||||
end
|
||||
else
|
||||
function rad:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return "Nuclear fallout", "Nuclear fallout"
|
||||
end
|
||||
end
|
||||
|
||||
local m_def = Material("stormfox2/hud/w_fallout.png")
|
||||
function rad.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function rad.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
return m_def
|
||||
end
|
||||
|
||||
-- Day --
|
||||
rad:SetSunStamp("topColor",Color(3.0, 102.9, 3.5), SF_SKY_DAY)
|
||||
rad:SetSunStamp("bottomColor",Color(20, 55, 25), SF_SKY_DAY)
|
||||
rad:SetSunStamp("duskColor",Color(3, 5.9, 3.5), SF_SKY_DAY)
|
||||
rad:SetSunStamp("duskScale",1, SF_SKY_DAY)
|
||||
rad:SetSunStamp("HDRScale",0.33, SF_SKY_DAY)
|
||||
-- Night
|
||||
rad:SetSunStamp("topColor",Color(0.4, 20.2, 0.54),SF_SKY_NIGHT)
|
||||
rad:SetSunStamp("bottomColor",Color(2.25, 25,2.25),SF_SKY_NIGHT)
|
||||
rad:SetSunStamp("duskColor",Color(.4, 1.2, .54), SF_SKY_NIGHT)
|
||||
rad:SetSunStamp("duskScale",0, SF_SKY_NIGHT)
|
||||
rad:SetSunStamp("HDRScale",0.1, SF_SKY_NIGHT)
|
||||
-- Sunset/rise
|
||||
rad:SetSunStamp("duskScale",0.26, SF_SKY_SUNSET)
|
||||
rad:SetSunStamp("duskScale",0.26, SF_SKY_SUNRISE)
|
||||
|
||||
if CLIENT then
|
||||
-- Snd
|
||||
local rain_light = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_light.ogg", SF_AMB_OUTSIDE, 1 )
|
||||
local rain_window = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_glass.ogg", SF_AMB_WINDOW, 0.1 )
|
||||
local rain_outside = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_outside.ogg", SF_AMB_NEAR_OUTSIDE, 0.1 )
|
||||
local rain_watersurf = StormFox2.Ambience.CreateAmbienceSnd( "ambient/water/water_run1.wav", SF_AMB_UNDER_WATER_Z, 0.1 )
|
||||
local rain_roof_wood = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_roof.ogg", SF_AMB_ROOF_WOOD, 0.1 )
|
||||
local rain_roof_metal = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_roof_metal.ogg", SF_AMB_ROOF_METAL, 0.1 )
|
||||
local rain_glass = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/rain_glass.ogg", SF_AMB_ROOF_GLASS, 0.1 )
|
||||
rad:AddAmbience( rain_light )
|
||||
rad:AddAmbience( rain_window )
|
||||
rad:AddAmbience( rain_outside )
|
||||
rad:AddAmbience( rain_watersurf )
|
||||
rad:AddAmbience( rain_roof_wood )
|
||||
rad:AddAmbience( rain_roof_metal )
|
||||
rad:AddAmbience( rain_glass )
|
||||
-- Edit watersurf
|
||||
rain_watersurf:SetFadeDistance(0,100)
|
||||
rain_watersurf:SetVolume( 0.05 )
|
||||
rain_watersurf:SetPlaybackRate(2)
|
||||
-- Edit rain_glass
|
||||
rain_roof_metal:SetFadeDistance(10,400)
|
||||
rain_glass:SetFadeDistance(10, 400)
|
||||
rain_window:SetFadeDistance(100, 200)
|
||||
-- Edit rain_outside
|
||||
rain_outside:SetFadeDistance(100, 200)
|
||||
|
||||
local m_rain = Material("stormfox2/raindrop.png")
|
||||
local m_rain2 = Material("stormfox2/effects/raindrop-multi2.png")
|
||||
local m_rain3 = Material("stormfox2/effects/raindrop-multi3.png")
|
||||
local m_rain_multi = Material("stormfox2/effects/snow-multi.png","noclamp smooth")
|
||||
function rad.TickSlow()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local L = StormFox2.Weather.GetLuminance()
|
||||
|
||||
rain_outside:SetVolume( P )
|
||||
rain_light:SetVolume( P )
|
||||
rain_window:SetVolume( P * 0.3 )
|
||||
rain_roof_wood:SetVolume( P * 0.3 )
|
||||
rain_roof_metal:SetVolume( P * 1 )
|
||||
rain_glass:SetVolume( P * 0.5 )
|
||||
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local speed = 0.72 + 0.36 * P
|
||||
StormFox2.Misc.rain_template:SetSpeed( speed )
|
||||
StormFox2.Misc.rain_template_medium:SetSpeed( speed )
|
||||
StormFox2.Misc.rain_template_medium:SetAlpha( L / 5)
|
||||
end
|
||||
local multi_dis = 1200
|
||||
local c = Color(150,250,150)
|
||||
function rad.Think()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local L = StormFox2.Weather.GetLuminance()
|
||||
local W = StormFox2.Wind.GetForce()
|
||||
if StormFox2.DownFall.GetGravity() < 0 then return end -- Rain can't come from the ground.
|
||||
|
||||
-- Set alpha
|
||||
local s = 1.22 + 1.56 * P
|
||||
StormFox2.Misc.rain_template:SetSize( s , 5.22 + 7.56 * P)
|
||||
StormFox2.Misc.rain_template:SetColor(c)
|
||||
StormFox2.Misc.rain_template:SetAlpha(math.min(15 + 4 * P + L,255))
|
||||
StormFox2.Misc.rain_template_medium:SetAlpha(math.min(15 + 4 * P + L,255) /3)
|
||||
-- Spawn rain particles
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template, 10, 700, 10 + P * 900, 5, vNorm ) or {} ) do
|
||||
v:SetSize( 1.22 + 1.56 * P * math.Rand(1,2), 5.22 + 7.56 * P )
|
||||
end
|
||||
-- Spawn distant rain
|
||||
if P > 0.15 then
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_medium, 250, 700, 10 + P * 500, 250, vNorm ) or {} ) do
|
||||
v:SetColor(c)
|
||||
local a = math.random(0,2)
|
||||
if a > 0 then
|
||||
if a > 1 then
|
||||
v:SetMaterial( m_rain2 )
|
||||
else
|
||||
v:SetMaterial(m_rain3 )
|
||||
end
|
||||
v:SetSize( 250, 250 )
|
||||
v:SetSpeed( v:GetSpeed() * math.Rand(1,2))
|
||||
else
|
||||
v:SetSize( 1.22 + 15.6 * P * math.Rand(1,3), 5.22 + 75.6 * P )
|
||||
end
|
||||
v:SetAlpha(math.min(15 + 4 * P + L,255) * 0.2)
|
||||
end
|
||||
end
|
||||
if P > (0.5 - W * 0.4) then
|
||||
local dis = math.random(900 - W * 100 - P * 500,multi_dis)
|
||||
local d = math.max(dis / multi_dis, 0.5)
|
||||
local s = math.Rand(0.5,1) * math.max(0.7,P) * 300 * d
|
||||
--StormFox2.Misc.rain_template_multi:SetAlpha(math.min(15 + 4 * P + L,255) * .2)
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_fog, dis, multi_dis * 2, (90 + P * (250 + W)) / 2, s, vNorm ) or {} ) do
|
||||
local d = v:GetDistance()
|
||||
if not d or d < 500 then
|
||||
v:SetSize( 225, 500 )
|
||||
else
|
||||
v:SetSize( d * .45, d)
|
||||
end
|
||||
|
||||
if math.random(0,1) == 1 then
|
||||
v:SetMaterial(m2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Render fallout
|
||||
local debri = Material("stormfox2/effects/terrain/fallout_water")
|
||||
local function renderD( a, b)
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
debri:SetFloat("$alpha",StormFox2.Weather.GetPercent())
|
||||
render.SetMaterial(debri)
|
||||
StormFox2.Environment.DrawWaterOverlay( b )
|
||||
end
|
||||
rad.PreDrawTranslucentRenderables = renderD
|
||||
|
||||
else
|
||||
-- Take dmg in rain, slowly
|
||||
local nt = 0
|
||||
function rad.Think()
|
||||
if not StormFox2.Setting.Get("weather_damage", true) then return end
|
||||
if nt < CurTime() then
|
||||
nt = CurTime() + 2
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamageType( DMG_RADIATION )
|
||||
dmg:SetDamage(10)
|
||||
dmg:SetAttacker( Entity(0) )
|
||||
dmg:SetInflictor( Entity(0) )
|
||||
local P = StormFox2.Weather.GetPercent() * 5
|
||||
for i,v in ipairs( player.GetAll() ) do
|
||||
if v:WaterLevel() > 0 then
|
||||
dmg:SetDamage((v:WaterLevel() ) * P)
|
||||
elseif StormFox2.Wind.IsEntityInWind(v) then
|
||||
dmg:SetDamage(P)
|
||||
else
|
||||
continue
|
||||
end
|
||||
v:TakeDamageInfo(dmg)
|
||||
v:EmitSound("player/geiger" .. math.random(1,3) .. ".wav")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Terrain
|
||||
local radt = StormFox2.Terrain.Create("radio")
|
||||
rad:SetTerrain( function(a) return StormFox2.Weather.GetPercent() > 0.5 and radt end )
|
||||
radt:SetGroundTexture("nature/toxicslime001a")
|
||||
-- Footsounds
|
||||
radt:MakeFootprints(true,{
|
||||
"player/footsteps/gravel1.wav",
|
||||
"player/footsteps/gravel2.wav",
|
||||
"player/footsteps/gravel3.wav",
|
||||
"player/footsteps/gravel4.wav"
|
||||
},"gravel.step")
|
||||
64
lua/stormfox2/weathers/fog.lua
Normal file
64
lua/stormfox2/weathers/fog.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- Rain and cloud is nearly the same.
|
||||
local fog = StormFox2.Weather.Add( "Fog" )
|
||||
fog:Set("fogDistance", 150)
|
||||
fog:Set("fogIndoorDistance", 600)
|
||||
if CLIENT then
|
||||
function fog.Think()
|
||||
local p = StormFox2.Weather.GetPercent()
|
||||
if p < 0.5 then return end
|
||||
// tTemplate, nMinDistance, nMaxDistance, nAimAmount, traceSize, vNorm, fFunc )
|
||||
local fc = StormFox2.Fog.GetColor()
|
||||
local c = Color(fc.r,fc.g,fc.b, 0)
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate(StormFox2.Misc.fog_template, 200, 900, 45 * p - 15, 250, vNorm ) or {} ) do
|
||||
v:SetColor( c )
|
||||
end
|
||||
end
|
||||
|
||||
function fog:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if nFraction < 0.2 then
|
||||
return language.GetPhrase('#sf_weather.clear'), 'Clear'
|
||||
elseif nFraction < 0.6 then
|
||||
return language.GetPhrase('#sf_weather.fog.low'), 'Haze'
|
||||
elseif nFraction < 0.8 then
|
||||
return language.GetPhrase('#sf_weather.fog.medium'), 'Fog'
|
||||
else
|
||||
return language.GetPhrase('#sf_weather.fog.high'), 'Thick Fog'
|
||||
end
|
||||
end
|
||||
else
|
||||
function fog:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
if nFraction < 0.2 then
|
||||
return 'Clear', 'Clear'
|
||||
elseif nFraction < 0.6 then
|
||||
return 'Haze', 'Haze'
|
||||
elseif nFraction < 0.8 then
|
||||
return 'Fog', 'Fog'
|
||||
else
|
||||
return 'Thick Fog', 'Thick Fog'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Fog icon
|
||||
do
|
||||
-- Icon
|
||||
local m_def = Material("stormfox2/hud/w_fog.png")
|
||||
function fog.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function fog.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
return m_def
|
||||
end
|
||||
end
|
||||
99
lua/stormfox2/weathers/lava.lua
Normal file
99
lua/stormfox2/weathers/lava.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
| 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 lava = StormFox2.Weather.Add( "Lava", "Cloud" )
|
||||
lava:Set("fogDistance", 2000)
|
||||
lava:Set("fogIndoorDistance", 3000)
|
||||
lava:Set("mapDayLight",0)
|
||||
local s = 10
|
||||
lava:SetSunStamp("bottomColor",Color(50, 2.5, 2.5), SF_SKY_DAY)
|
||||
lava:SetSunStamp("bottomColor",Color(2.25, .225,.225),SF_SKY_NIGHT)
|
||||
|
||||
if CLIENT then
|
||||
function lava:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return language.GetPhrase('sf_weather.lava')
|
||||
end
|
||||
else
|
||||
function lava:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return "Lava", "Lava"
|
||||
end
|
||||
end
|
||||
local m_def = Material("stormfox2/hud/w_lava.png")
|
||||
function lava.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function lava.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
return m_def
|
||||
end
|
||||
|
||||
-- Terrain
|
||||
local t_lava = StormFox2.Terrain.Create("lava")
|
||||
local a = function()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
return P > 0.5 and t_lava
|
||||
end
|
||||
lava:SetTerrain( a )
|
||||
t_lava:SetGroundTexture("stormfox2/effects/terrain/lava_ground", true)
|
||||
|
||||
-- Downfall & Snd
|
||||
if CLIENT then
|
||||
local lava_snd = StormFox2.Ambience.CreateAmbienceSnd( "stormfox2/amb/lava.ogg", SF_AMB_OUTSIDE, 0.4 )
|
||||
local m_lava = Material("stormfox2/effects/lava_particle")
|
||||
lava:AddAmbience( lava_snd )
|
||||
local p_d = Material("stormfox2/effects/lava_particle2")
|
||||
local lava_particles = StormFox2.DownFall.CreateTemplate(p_d, true)
|
||||
lava_particles:SetFadeIn( true )
|
||||
lava_particles:SetRandomAngle(0.1)
|
||||
function lava:Think()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
lava_snd:SetVolume( P * .4 )
|
||||
|
||||
local l = math.min(255, StormFox2.Weather.GetLuminance() * 7)
|
||||
lava_particles:SetColor(Color(l,l,l))
|
||||
lava_particles:SetSpeed(.3) -- Makes the start position
|
||||
local dis = math.random(100,1500)
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( lava_particles, 200, dis, P * 800, 5, vNorm ) or {} ) do
|
||||
local s = math.Rand(10,200)
|
||||
v:SetSize( s, s )
|
||||
v:SetSpeed( math.Rand(.05, .15) )
|
||||
if math.random(1,2) == 1 then
|
||||
v:SetMaterial(m_lava)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Render water debri
|
||||
local debri = Material("stormfox2/effects/terrain/lava_water")
|
||||
local function renderD( a, b)
|
||||
render.SetMaterial(debri)
|
||||
StormFox2.Environment.DrawWaterOverlay( b )
|
||||
end
|
||||
lava.PreDrawTranslucentRenderables = renderD
|
||||
end
|
||||
|
||||
-- Burn
|
||||
if SERVER then
|
||||
t_lava:MakeFootprints( false, nil, nil, function(ent, foot, SoundName, sTex, bReplace)
|
||||
if not ent or not IsValid(ent) then return end
|
||||
if not bReplace then return end
|
||||
if ent.Health and ent:Health() <= 1 then return end
|
||||
if not StormFox2.Setting.Get("weather_damage", true) then return end
|
||||
if math.random(1, 10) <= 9 then
|
||||
local burn = DamageInfo()
|
||||
burn:SetDamage( math.random(5, 10) )
|
||||
burn:SetDamageType(DMG_BURN)
|
||||
burn:SetInflictor(game.GetWorld())
|
||||
burn:SetAttacker(game.GetWorld())
|
||||
burn:SetDamagePosition( ent:GetPos() )
|
||||
ent:TakeDamageInfo( burn )
|
||||
else
|
||||
ent:Ignite(1, 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
188
lua/stormfox2/weathers/sand.lua
Normal file
188
lua/stormfox2/weathers/sand.lua
Normal file
@@ -0,0 +1,188 @@
|
||||
--[[
|
||||
| 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 max = math.max
|
||||
local sand = StormFox2.Weather.Add( "Sandstorm" )
|
||||
-- Display name
|
||||
if CLIENT then
|
||||
function sand:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return language.GetPhrase('sf_weather.sandstorm')
|
||||
end
|
||||
else
|
||||
function sand:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return "Sandstorm"
|
||||
end
|
||||
end
|
||||
-- Icon
|
||||
local m_def = Material("stormfox2/hud/w_sand.png")
|
||||
function sand.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function sand.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
return m_def
|
||||
end
|
||||
-- Sky
|
||||
-- Day --
|
||||
sand:SetSunStamp("bottomColor",Color(255,216,170), SF_SKY_SUNRISE)
|
||||
-- sand:SetSunStamp("duskColor",Color(3, 2.9, 3.5), SF_SKY_DAY)
|
||||
-- sand:SetSunStamp("duskScale",1, SF_SKY_DAY)
|
||||
sand:SetSunStamp("HDRScale",0.33, SF_SKY_DAY)
|
||||
-- Night
|
||||
sand:SetSunStamp("bottomColor",Color(255,216,170), SF_SKY_SUNSET)
|
||||
-- Sunset/rise
|
||||
-- sand:Set("duskScale",0.26)
|
||||
|
||||
sand:Set("starFade",0)
|
||||
sand:Set("skyVisibility",function(stamp)
|
||||
local v = (StormFox2.Weather.GetPercent() - 0.5) * 2
|
||||
return (1 - v) * 70
|
||||
end)
|
||||
sand:Set("clouds",function( stamp)
|
||||
return (StormFox2.Weather.GetPercent() - 0.5) * 2
|
||||
end)
|
||||
|
||||
sand:Set("mapDayLight",70) -- 70% maplight at max
|
||||
-- Fog
|
||||
sand:Set("fogIndoorDistance", 5500)
|
||||
sand:Set("fogDistance", function()
|
||||
local wF = StormFox2.Wind.GetForce()
|
||||
if wF <= 0 then return 4000 end
|
||||
return max(4000 - 55 * wF,0)
|
||||
end)
|
||||
-- Terrain
|
||||
local sand_t = StormFox2.Terrain.Create("sand")
|
||||
sand:SetTerrain( function()
|
||||
return StormFox2.Weather.GetPercent() > 0.5 and sand_t
|
||||
end )
|
||||
sand_t:SetGroundTexture("nature/sandfloor009a", true)
|
||||
-- Footprints
|
||||
sand_t:MakeFootprints(true,{
|
||||
"player/footsteps/sand1.wav",
|
||||
"player/footsteps/sand2.wav",
|
||||
"player/footsteps/sand3.wav",
|
||||
"player/footsteps/sand4.wav"
|
||||
},"sand.step")
|
||||
if CLIENT then
|
||||
-- Load mats
|
||||
local t = {}
|
||||
for i = 1, 16 do
|
||||
local c = i
|
||||
if c < 10 then
|
||||
c = "0" .. c
|
||||
end
|
||||
local m = Material("particle/smokesprites_00" .. c)
|
||||
if m:IsError() then continue end
|
||||
table.insert(t, m)
|
||||
end
|
||||
if #t > 0 then -- Make sure we at least got 1 material
|
||||
local function makeCloud(vPos, s, vVec)
|
||||
local p = StormFox2.DownFall.AddParticle( table.Random(t), vPos, false )
|
||||
p:SetStartSize(s / 4)
|
||||
p:SetEndSize(50 + s)
|
||||
p:SetDieTime(math.Rand(2,3))
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(255, 120 + s / 3))
|
||||
p:SetVelocity(vector_up * math.random(5,15))
|
||||
local c = StormFox2.Fog.GetColor()
|
||||
p:SetColor(c.r, c.g, c.b)
|
||||
p:SetRoll(math.random(360))
|
||||
end
|
||||
local limiter = 0
|
||||
sand_t:MakeFootprints( false, nil, nil, function(ent, foot, SoundName, sTex, bReplace)
|
||||
if not ent or not IsValid(ent) then return end
|
||||
if not bReplace then return end
|
||||
if ent.Health and ent:Health() <= 1 then return end
|
||||
local s = ent:GetVelocity():Length()
|
||||
if s < 200 then return end
|
||||
s = math.min(s, 1500)
|
||||
if limiter > CurTime() then return end
|
||||
limiter = CurTime() + 0.2 -- Limit it to about 15 particles
|
||||
local c = (s - 150)
|
||||
makeCloud(ent:GetPos(), c / 3)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Particles
|
||||
function sand.Think()
|
||||
local min,random = math.min,math.random
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local L = StormFox2.Weather.GetLuminance()
|
||||
local W = StormFox2.Wind.GetForce()
|
||||
if StormFox2.DownFall.GetGravity() < 0 then return end -- Clouds can't come from the ground.
|
||||
StormFox2.Misc.rain_template_fog:SetAlpha( L )
|
||||
-- Set alpha
|
||||
local s = 1.22 + 1.56 * P
|
||||
local max_fog = W
|
||||
local sand_distance = min(random(300,900), StormFox2.Fog.GetEnd())
|
||||
|
||||
local fc = StormFox2.Fog.GetColor()
|
||||
local c = Color(fc.r * 0.95 ,fc.g * 0.95, fc.b * 0.95, 0)
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.fog_template, sand_distance, sand_distance * 2 , 30 + max_fog, 200, vNorm ) or {} ) do
|
||||
v:SetColor( c )
|
||||
end
|
||||
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( StormFox2.Misc.rain_template_fog, sand_distance, sand_distance * 2 , max_fog, 200, vNorm ) or {} ) do
|
||||
local d = v:GetDistance()
|
||||
if not d or d < 500 then
|
||||
v:SetSize( 225, 500 )
|
||||
else
|
||||
v:SetSize( d * .45, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Depth filter
|
||||
local up = Vector(0,0,1)
|
||||
|
||||
local function setMaterialRoll(mat, roll, u, v)
|
||||
local matrix = Matrix()
|
||||
local w = mat:Width()
|
||||
local h = mat:Height()
|
||||
matrix:SetAngles(Angle(0,roll,0))
|
||||
matrix:Translate(Vector(u, v, 0))
|
||||
mat:SetMatrix("$basetexturetransform", matrix)
|
||||
end
|
||||
local sx,sy = 0,0
|
||||
local rx,ry,rx2,ry2 = 0,0,0,0
|
||||
local mat = Material("stormfox2/effects/rainstorm.png", "noclamp")
|
||||
function sand.DepthFilter(w, h, a)
|
||||
a = (a - 0.50) * 2
|
||||
if a <= 0 then return end
|
||||
local windDir = (-StormFox2.Wind.GetNorm()):Angle()
|
||||
|
||||
local ad = math.AngleDifference(StormFox2.Wind.GetYaw() + 180, StormFox2.util.GetCalcView().ang.y)
|
||||
local ada = math.sin(math.rad(ad))
|
||||
-- 0 = directly into the wind
|
||||
-- 1 = directly to the side of the wind
|
||||
|
||||
-- 0 = not moving at all
|
||||
-- 1 = max movment
|
||||
local A = EyeAngles():Forward()
|
||||
local B = windDir:Forward()
|
||||
local D = math.abs(A:Dot(B))
|
||||
local C = 1 - D
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local W = math.min(1, StormFox2.Wind.GetForce() / 60)
|
||||
|
||||
local B2 = windDir:Right()
|
||||
local D2 = (A:Dot(B2))
|
||||
|
||||
local WP = math.min(1, P) -- 0 - 1 Wimdy
|
||||
local wind_x = ada * -C * 4 * WP
|
||||
local wind_y = -8 * math.max(0.5, WP)
|
||||
local roll = (windDir.p - 270) * -D2 * 1.4
|
||||
rx = (rx + FrameTime() * wind_x) % 1
|
||||
ry = (ry + FrameTime() * wind_y) % 1
|
||||
setMaterialRoll(mat, 180 - roll + 3, rx, ry)
|
||||
surface.SetMaterial( mat )
|
||||
surface.SetDrawColor( Color(255,255,255,154 * a * math.max(0.1, W) * WP * math.max(C,0)) )
|
||||
local s,s2 = 1.7, 1.8
|
||||
surface.DrawTexturedRectUV( 0, 0, ScrW(), ScrH(), 0, 0,0 + s, 0 + s)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user