This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent c4d91bf369
commit 324f19217d
8040 changed files with 1853423 additions and 21 deletions

View 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

File diff suppressed because it is too large Load Diff

View 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)

View 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

View 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

View 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

View 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?
]]

View 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)

View 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)

View 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

View 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

View 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

View 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

View 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)

View 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)

View 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")