mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
275
lua/stormfox2/lib/cl_ambiencesnd.lua
Normal file
275
lua/stormfox2/lib/cl_ambiencesnd.lua
Normal file
@@ -0,0 +1,275 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
SF_AMB_SND = SF_AMB_SND or {}
|
||||
SF_AMB_CHANNEL = SF_AMB_CHANNEL or {} -- [snd]{station, target_vol, current_vol}
|
||||
|
||||
StormFox2.Ambience = {}
|
||||
|
||||
--[[
|
||||
- Outside Constant
|
||||
- Near Outside 3D
|
||||
- Near Window By Distance to nearest
|
||||
- Roof By Distance to nearest
|
||||
- Glass Roof (Like window) By Distance to nearest
|
||||
- Metal Roof By Distance to nearest
|
||||
]]
|
||||
|
||||
--[[ Enums
|
||||
SF_AMB_CONSTANT = 0
|
||||
SF_AMB_DISTANCE = 1
|
||||
SF_AMB_FAKE3D = 2 -- Pans the sound towards the point
|
||||
SF_AMB_USE3D = 3
|
||||
]]
|
||||
|
||||
SF_AMB_OUTSIDE = 0 -- CONSTANT VOLUME
|
||||
SF_AMB_NEAR_OUTSIDE = 1 -- DISTANCE VOLUME
|
||||
SF_AMB_WINDOW = 2 -- DISTANCE VOLUME
|
||||
SF_AMB_UNDER_WATER = 3 -- CONSTANT VOLUME
|
||||
SF_AMB_UNDER_WATER_Z = 4 -- Z-DISTANCE VOLUME (The distance to surface)
|
||||
SF_AMB_ROOF_ANY = 5 -- Z-DISTANCE (SF_AMB_ROOF_CONCRETE and SF_AMB_ROOF_GROUND will be ignored)
|
||||
SF_AMB_ROOF_GLASS = 6 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_METAL = 7 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_WOOD = 8 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_CONCRETE = 9 -- Z-DISTANCE
|
||||
SF_AMB_ROOF_GROUND = 10-- Z-DISTANCE (Default roof)
|
||||
SF_AMB_ROOF_WATER = 11-- Z-DISTANCE
|
||||
|
||||
-- Smooth the volume of SF_AMB_CHANNEL
|
||||
hook.Add("Think", "StormFox2.Ambiences.Smooth", function()
|
||||
for snd,t in pairs( SF_AMB_CHANNEL ) do
|
||||
if not IsValid( t[1] ) then -- In case something goes wrong. Delete the channel
|
||||
SF_AMB_CHANNEL[snd] = nil
|
||||
continue
|
||||
end
|
||||
-- Calc the new volume
|
||||
local c_vol = t[3]
|
||||
local newvol = math.Approach( c_vol, t[2], FrameTime() )
|
||||
if c_vol == newvol then continue end
|
||||
if newvol <= 0 then
|
||||
-- Stop the sound and remove channel
|
||||
t[1]:Stop()
|
||||
SF_AMB_CHANNEL[snd] = nil
|
||||
else
|
||||
if system.HasFocus() then -- We don't want sound playing when gmod is unfocused.
|
||||
t[1]:SetVolume( newvol )
|
||||
else
|
||||
t[1]:SetVolume( 0 )
|
||||
end
|
||||
SF_AMB_CHANNEL[snd][3] = newvol
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local AMB_LOAD = {}
|
||||
-- Handles the sound-channel.
|
||||
local function RequestChannel( snd )
|
||||
if AMB_LOAD[snd] then return end -- Already loading, or error
|
||||
if SF_AMB_CHANNEL[snd] then return end -- Already loaded
|
||||
AMB_LOAD[snd] = true
|
||||
sound.PlayFile( snd, "noblock noplay", function( station, errCode, errStr )
|
||||
if ( IsValid( station ) ) then
|
||||
SF_AMB_CHANNEL[snd] = {station, 0.1, 0}
|
||||
station:SetVolume( 0 )
|
||||
station:EnableLooping( true )
|
||||
station:Play()
|
||||
AMB_LOAD[snd] = nil -- Allow it to be loaded again
|
||||
else
|
||||
if errCode == 1 then
|
||||
StormFox2.Warning("Sound Error! [1] Memory error.")
|
||||
elseif errCode == 2 then
|
||||
StormFox2.Warning("Sound Error! [2] Unable to locate or open: " .. snd .. ".")
|
||||
else
|
||||
StormFox2.Warning("Sound Error! [" .. errCode .. "] " .. errStr .. ".")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local snd_meta = {}
|
||||
snd_meta.__index = snd_meta
|
||||
|
||||
---Creates an ambience sound and returns a sound-object.
|
||||
---@param snd string
|
||||
---@param SF_AMB_TYPE number
|
||||
---@param vol_scale? number
|
||||
---@param min? number
|
||||
---@param max? number
|
||||
---@param playrate? number
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.Ambience.CreateAmbienceSnd( snd, SF_AMB_TYPE, vol_scale, min, max, playrate )
|
||||
local t = {}
|
||||
t.snd = "sound/" .. snd
|
||||
t.m_vol = vol_scale or 1
|
||||
t.min = min or 60
|
||||
t.max = max or 300
|
||||
t.SF_AMB_TYPE = SF_AMB_TYPE or SF_AMB_OUTSIDE
|
||||
t.playbackrate = playrate or 1
|
||||
setmetatable( t , snd_meta )
|
||||
return t
|
||||
end
|
||||
|
||||
---Returns the current sound channels / data.
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.Ambience.DebugList()
|
||||
return SF_AMB_CHANNEL
|
||||
end
|
||||
-- Sets the scale of the sound
|
||||
function snd_meta:SetVolume( num )
|
||||
self.m_vol = math.Clamp(num, 0, 2) -- Just in case
|
||||
end
|
||||
-- Doesn't work on sounds with SF_AMB_OUTSIDE or SF_AMB_UNDER_WATER
|
||||
function snd_meta:SetFadeDistance( min, max )
|
||||
self.min = min
|
||||
self.max = max
|
||||
end
|
||||
-- Set playback rate.
|
||||
function snd_meta:SetPlaybackRate( n )
|
||||
self.playbackrate = n or 1
|
||||
end
|
||||
-- Adds ambience for weather
|
||||
hook.Add("stormfox2.preloadweather", "StormFox2.Amb.Create", function( w_meta )
|
||||
function w_meta:AddAmbience( amb_object )
|
||||
if not self.ambience_tab then self.ambience_tab = {} end
|
||||
table.insert(self.ambience_tab, amb_object)
|
||||
end
|
||||
function w_meta:ClearAmbience()
|
||||
self.ambience_tab = {}
|
||||
end
|
||||
hook.Remove("stormfox2.preloadweather", "StormFox2.Amb.Create")
|
||||
end)
|
||||
-- Applies the ambience sound
|
||||
local function check(SF_AMB_TYPE, env)
|
||||
if SF_AMB_TYPE == SF_AMB_NEAR_OUTSIDE and env.nearest_outside then return env.nearest_outside end
|
||||
if SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_window then return env.nearest_window end
|
||||
end
|
||||
|
||||
local p_br = {}
|
||||
-- Forces a sound to play
|
||||
local fP
|
||||
|
||||
---Insers ambience sound and forces it to play.
|
||||
---@param snd string
|
||||
---@param nVolume number
|
||||
---@param playbackSpeed number
|
||||
---@client
|
||||
function StormFox2.Ambience.ForcePlay( snd, nVolume, playbackSpeed )
|
||||
if string.sub(snd, 0, 6) ~= "sound/" then
|
||||
snd = "sound/" .. snd
|
||||
end
|
||||
fP[snd] = nVolume
|
||||
p_br[snd] = playbackSpeed or 1
|
||||
end
|
||||
hook.Add("Think", "StormFox2.Ambiences.Logic", function()
|
||||
if not StormFox2 or not StormFox2.Weather or not StormFox2.Weather.GetCurrent then return end
|
||||
local c = StormFox2.Weather.GetCurrent()
|
||||
local v_pos = StormFox2.util.GetCalcView().pos
|
||||
local env = StormFox2.Environment.Get()
|
||||
-- Set all target volume to 0
|
||||
for _,t2 in pairs( SF_AMB_CHANNEL ) do
|
||||
t2[2] = 0
|
||||
end
|
||||
-- Generate a list of all sounds the client should hear. And set the the volume
|
||||
local t = {}
|
||||
if c.ambience_tab and StormFox2.Setting.SFEnabled() then
|
||||
for _,amb_object in ipairs( c.ambience_tab ) do
|
||||
local c_vol = t[amb_object.snd] or 0
|
||||
-- WATER
|
||||
if env.in_water then -- All sounds gets ignored in water. Exp SF_AMB_INWATER
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_INWATER then
|
||||
if c_vol > amb_object.m_vol then
|
||||
continue
|
||||
end
|
||||
c_vol = amb_object.m_vol
|
||||
elseif env.outside and amb_object.SF_AMB_TYPE == SF_AMB_UNDER_WATER_Z then
|
||||
local dis = env.in_water - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
-- OUTSIDE
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_OUTSIDE and env.outside then
|
||||
if c_vol > amb_object.m_vol then
|
||||
continue
|
||||
end
|
||||
c_vol = amb_object.m_vol -- Outside is a constant volume
|
||||
-- ROOFS
|
||||
elseif amb_object.SF_AMB_TYPE >= SF_AMB_ROOF_ANY and amb_object.SF_AMB_TYPE <= SF_AMB_ROOF_WATER then
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_ANY and env.roof_z then
|
||||
if env.roof_type ~= SF_AMB_ROOF_CONCRETE and env.roof_type ~= SF_AMB_ROOF_GROUND then
|
||||
local dis = env.roof_z - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
elseif env.roof_z and env.roof_type then
|
||||
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GROUND and env.roof_type == SF_DOWNFALL_HIT_GROUND then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GLASS and env.roof_type == SF_DOWNFALL_HIT_GLASS then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_METAL and env.roof_type == SF_DOWNFALL_HIT_METAL then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WOOD and env.roof_type == SF_DOWNFALL_HIT_WOOD then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_CONCRETE and env.roof_type == SF_DOWNFALL_HIT_CONCRETE then
|
||||
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WATER and env.roof_type == SF_DOWNFALL_HIT_WATER then
|
||||
else
|
||||
continue
|
||||
end
|
||||
local dis = env.roof_z - v_pos.z
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
else
|
||||
local pos = check( amb_object.SF_AMB_TYPE, env )
|
||||
if not pos then continue end
|
||||
local dis = pos:Distance( v_pos )
|
||||
--if amb_object.SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_outside then
|
||||
-- dis = math.max(dis, 250 - env.nearest_outside:Distance(v_pos))
|
||||
|
||||
--end
|
||||
if dis > amb_object.max then continue end -- Too far away
|
||||
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
||||
if vol <= 0 then continue end -- Vol too low
|
||||
if c_vol > vol then
|
||||
continue
|
||||
end
|
||||
c_vol = vol
|
||||
end
|
||||
if c_vol > 0 then
|
||||
t[amb_object.snd] = c_vol
|
||||
p_br[amb_object.snd] = amb_object.playbackrate
|
||||
end
|
||||
end
|
||||
end
|
||||
fP = t
|
||||
hook.Run("StormFox2.Ambiences.OnSound")
|
||||
-- Set the target volume
|
||||
for snd, vol in pairs( t ) do
|
||||
if not SF_AMB_CHANNEL[snd] then -- Request to create the sound channel
|
||||
RequestChannel( snd )
|
||||
else
|
||||
SF_AMB_CHANNEL[snd][2] = vol -- Set the target volume
|
||||
if IsValid( SF_AMB_CHANNEL[snd][1] ) then
|
||||
if SF_AMB_CHANNEL[snd][1]:GetState() == 0 then -- Somehow stopped
|
||||
SF_AMB_CHANNEL[snd][1]:Play()
|
||||
end
|
||||
if SF_AMB_CHANNEL[snd][1]:GetPlaybackRate() ~= p_br[snd] then
|
||||
SF_AMB_CHANNEL[snd][1]:SetPlaybackRate(p_br[snd])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
236
lua/stormfox2/lib/cl_defaultparticles.lua
Normal file
236
lua/stormfox2/lib/cl_defaultparticles.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Rain and show particles are a bit large. So we init them here
|
||||
|
||||
if not StormFox2.Misc then StormFox2.Misc = {} end
|
||||
local m_snow = Material("particle/snow")
|
||||
local m_snow_multi = Material("stormfox2/effects/snow-multi.png")
|
||||
local m_rain = Material("stormfox2/effects/raindrop.png")
|
||||
local m_rain_medium = Material("stormfox2/effects/raindrop2.png")
|
||||
local m_rain_fog = Material("particle/particle_smokegrenade")
|
||||
local rainsplash_w = Material("effects/splashwake3")
|
||||
local rainsplash = Material("stormfox2/effects/rain_splash")
|
||||
local m_noise = Material("particle/particle_noisesphere")
|
||||
local m_fog = Material("particle/smokesprites_0014")
|
||||
|
||||
-- Hit particles
|
||||
local function MakeRing( vPos, vNormal, L )
|
||||
local p = StormFox2.DownFall.AddParticle( rainsplash_w, vPos, true )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
p:SetStartSize(8)
|
||||
p:SetEndSize(40)
|
||||
p:SetDieTime(1)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(255,25 + math.random(7,10) + L * 0.9))
|
||||
end
|
||||
local function MakeSplash( vPos, vNormal, L, Part )
|
||||
local p = StormFox2.DownFall.AddParticle( rainsplash, vPos, false )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
local _,s = Part:GetSize()
|
||||
p:SetStartSize(s / 10)
|
||||
p:SetEndSize(s / 2.5)
|
||||
p:SetDieTime(0.15)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(105, 30 + L * 0.9))
|
||||
end
|
||||
local function MakeSnowflake( vPos, vNormal, L, Part )
|
||||
local p = StormFox2.DownFall.AddParticle( m_snow, vPos - vNormal, false )
|
||||
p:SetAngles(vNormal:Angle())
|
||||
p:SetStartSize(math.min(2,Part:GetSize()))
|
||||
p:SetEndSize(0)
|
||||
p:SetDieTime(5)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(math.min(255, 10 + L))
|
||||
end
|
||||
|
||||
local pT = function(self)
|
||||
local n = math.min(15, StormFox2.Weather.GetLuminance() * 0.75)
|
||||
if self:GetLifeTime() < self:GetDieTime() * .25 then
|
||||
self:SetStartAlpha(0)
|
||||
self:SetEndAlpha( n * 8 )
|
||||
elseif self:GetLifeTime() < self:GetDieTime() * .5 then
|
||||
self:SetStartAlpha(n)
|
||||
self:SetEndAlpha( n )
|
||||
else
|
||||
self:SetStartAlpha(n * 2)
|
||||
self:SetEndAlpha( 0 )
|
||||
end
|
||||
self:SetNextThink( CurTime() )
|
||||
end
|
||||
|
||||
local LM = 0
|
||||
local vector_zero = Vector(0,0,0)
|
||||
local function MakeMist( vPos, L, Part)
|
||||
if LM > CurTime() then return end
|
||||
--LM = CurTime() + 0.1
|
||||
local w = StormFox2.Wind.GetVector()
|
||||
local v = Vector(w.x * 8 + math.Rand(-10, 10), w.y * 8 + math.Rand(-10, 10) ,math.Rand(0, 10))
|
||||
local ss = math.Rand(75,180)
|
||||
local es = math.Rand(75,180)
|
||||
local p = StormFox2.DownFall.AddParticle( m_rain_fog, vPos + Vector(0,0,math.max(es,ss) / 2), false )
|
||||
if not p then return end
|
||||
p:SetAirResistance(0)
|
||||
p:SetNextThink( CurTime() )
|
||||
p:SetDieTime( math.random(10, 15))
|
||||
p:SetRoll( math.Rand(0,360) )
|
||||
p:SetStartSize(ss)
|
||||
p:SetEndSize(es)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(0)
|
||||
p:SetThinkFunction(pT)
|
||||
local c = Part:GetColor() or color_white
|
||||
p:SetColor( c.r, c.g, c.b )
|
||||
p:SetVelocity(v)
|
||||
p:SetCollide( true )
|
||||
p:SetCollideCallback( function( part ) --This is an in-line function
|
||||
part:SetVelocity(vector_zero)
|
||||
p:SetLifeTime(25)
|
||||
end )
|
||||
end
|
||||
|
||||
-- Make big cloud particles size shared, to fix size hitting
|
||||
|
||||
local init = function()
|
||||
local fog_template = StormFox2.DownFall.CreateTemplate(m_fog, false)
|
||||
StormFox2.Misc.fog_template = fog_template
|
||||
--fog_template:SetSpeed(0.1)
|
||||
fog_template:SetSize(250, 250)
|
||||
function fog_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
elseif nHitType == SF_DOWNFALL_HIT_GLASS then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
|
||||
local rain_template = StormFox2.DownFall.CreateTemplate(m_rain, true)
|
||||
local rain_template_medium =StormFox2.DownFall.CreateTemplate(m_rain_medium,true)
|
||||
local rain_template_fog = StormFox2.DownFall.CreateTemplate(m_rain_fog, true)
|
||||
local snow_template = StormFox2.DownFall.CreateTemplate(m_snow, false, false)
|
||||
local snow_template_multi = StormFox2.DownFall.CreateTemplate(m_snow_multi, true)
|
||||
local fog_template = StormFox2.DownFall.CreateTemplate(m_rain, true) -- A "empty" particle that hits the ground, and create a fog particle on-hit.
|
||||
StormFox2.Misc.rain_template = rain_template
|
||||
StormFox2.Misc.rain_template_fog = rain_template_fog
|
||||
StormFox2.Misc.rain_template_medium = rain_template_medium
|
||||
StormFox2.Misc.snow_template = snow_template
|
||||
StormFox2.Misc.snow_template_multi = snow_template_multi
|
||||
StormFox2.Misc.fog_template = fog_template
|
||||
|
||||
--rain_template_medium
|
||||
rain_template_medium:SetFadeIn( true )
|
||||
rain_template_medium:SetSize(20,40)
|
||||
rain_template_medium:SetRenderHeight(800)
|
||||
rain_template_medium:SetAlpha(20)
|
||||
|
||||
--rain_template_fog
|
||||
rain_template_fog:SetFadeIn( true )
|
||||
rain_template_fog:SetSize(150, 600)
|
||||
rain_template_fog:SetRandomAngle(0.15)
|
||||
rain_template_fog:SetSpeed( 0.5 )
|
||||
|
||||
snow_template:SetRandomAngle(0.4)
|
||||
snow_template:SetSpeed( 1 * 0.15)
|
||||
snow_template:SetSize(5,5)
|
||||
snow_template_multi:SetFadeIn( true )
|
||||
snow_template_multi:SetSize(300,300)
|
||||
|
||||
--snow_template_multi:SetRenderHeight( 600 )
|
||||
snow_template_multi:SetRandomAngle(0.3)
|
||||
|
||||
-- Think functions:
|
||||
function rain_template_fog:Think()
|
||||
local P = StormFox2.Weather.GetPercent()
|
||||
local fC = StormFox2.Fog.GetColor()
|
||||
local L = math.min(StormFox2.Weather.GetLuminance(), 100)
|
||||
local TL = StormFox2.Thunder.GetLight() / 2
|
||||
local speed = 0.162 * P + 0.324
|
||||
self:SetColor( Color(fC.r + TL + 15, fC.g + TL + 15, fC.b + TL + 15) )
|
||||
self:SetAlpha( math.min(255, math.max(0, (P - 0.5) * 525 )) )
|
||||
self:SetSpeed( speed )
|
||||
end
|
||||
|
||||
-- Particle Explosion
|
||||
-- Make "rain" explosion at rain particles
|
||||
function rain_template:OnExplosion( vExPos, nDisPercent, iRange, iMagnetide )
|
||||
local e_ang = (self:GetPos() - vExPos):Angle():Forward()
|
||||
local boost = nDisPercent * 5
|
||||
local p = StormFox2.DownFall.AddParticle( "effects/splash1", vExPos + e_ang * iRange *nDisPercent , false )
|
||||
p:SetStartSize(math.random(32, 20))
|
||||
p:SetEndSize(5)
|
||||
p:SetDieTime(2.5)
|
||||
p:SetEndAlpha(0)
|
||||
p:SetStartAlpha(6)
|
||||
p:SetGravity( physenv.GetGravity() * 2 )
|
||||
p:SetVelocity( e_ang * iMagnetide * boost)
|
||||
p:SetAirResistance(3)
|
||||
p:SetCollide(true)
|
||||
p:SetRoll(math.random(360))
|
||||
p:SetCollideCallback(function( part )
|
||||
part:SetDieTime(0)
|
||||
end)
|
||||
end
|
||||
rain_template_medium.OnExplosion = rain_template.OnExplosion
|
||||
|
||||
-- Particle Hit
|
||||
function snow_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSnowflake( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
function rain_template:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if nHitType == SF_DOWNFALL_HIT_WATER then
|
||||
MakeRing( vPos, vNormal, L )
|
||||
elseif nHitType == SF_DOWNFALL_HIT_GLASS then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
else -- if nHitType == SF_DOWNFALL_HIT_GROUND then
|
||||
MakeSplash( vPos, vNormal, L, zPart )
|
||||
end
|
||||
end
|
||||
function rain_template_fog:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
if math.random(1,3)> 2 then return end
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
local i = 0
|
||||
function snow_template_multi:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
if i < 10 then
|
||||
i = i + 1
|
||||
return
|
||||
end
|
||||
i = 0
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
|
||||
fog_template:SetSize(512,512)
|
||||
fog_template:SetSpeed(5)
|
||||
fog_template:SetAlpha(0)
|
||||
function fog_template:OnHit( vPos, vNormal, nHitType, zPart)
|
||||
local L = StormFox2.Weather.GetLuminance() - 10
|
||||
MakeMist( vPos, L, zPart)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("stormfox2.postlib", "stormfox2.loadParticles", init)
|
||||
if StormFox2.DownFall and StormFox2.DownFall.CreateTemplate then
|
||||
init()
|
||||
end
|
||||
36
lua/stormfox2/lib/cl_entities.lua
Normal file
36
lua/stormfox2/lib/cl_entities.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Returns an explosion from x position
|
||||
net.Receive("StormFox2.entity.explosion", function(len)
|
||||
local pos = net.ReadVector()
|
||||
local iRadiusOverride = net.ReadInt(16)
|
||||
local iMagnitude = net.ReadUInt(16)
|
||||
hook.Run("StormFox2.Entitys.OnExplosion", pos, iRadiusOverride, iMagnitude)
|
||||
end)
|
||||
|
||||
StormFox2.Ent = {}
|
||||
hook.Add("stormfox2.postlib", "stormfox2.c_ENT",function()
|
||||
StormFox2.Ent.env_skypaints = true -- Always
|
||||
StormFox2.Ent.env_fog_controllers = true -- Always
|
||||
StormFox2.Ent.light_environments = false -- Special
|
||||
for k,v in ipairs( StormFox2.Map.FindClass("light_environment") ) do
|
||||
if v.targetname then
|
||||
StormFox2.Ent.light_environments = true
|
||||
break
|
||||
end
|
||||
end
|
||||
StormFox2.Ent.shadow_controls = #StormFox2.Map.FindClass("shadow_control") > 0
|
||||
StormFox2.Ent.env_tonemap_controllers = #StormFox2.Map.FindClass("env_tonemap_controller") > 0
|
||||
StormFox2.Ent.env_winds = #StormFox2.Map.FindClass("env_wind") > 0
|
||||
StormFox2.Ent.env_tonemap_controller = #StormFox2.Map.FindClass("env_tonemap_controller") > 0
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.c_ENT")
|
||||
end)
|
||||
130
lua/stormfox2/lib/cl_fonts.lua
Normal file
130
lua/stormfox2/lib/cl_fonts.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
surface.CreateFont( "SF_Display_H", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 30,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Display_H2", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 20,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Display_H3", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = true,
|
||||
size = 14,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SF_Menu_H2", {
|
||||
font = "coolvetica", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 20,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "SkyFox-DigitalClock", {
|
||||
font = "Arial", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 50,
|
||||
weight = 500,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = false,
|
||||
} )
|
||||
|
||||
surface.CreateFont("SF2.W_Button", {
|
||||
font = "Tahoma",
|
||||
size = 15,
|
||||
weight = 1500,
|
||||
})
|
||||
|
||||
-- Tool
|
||||
surface.CreateFont( "sf_tool_large", {
|
||||
font = "Verdana", -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = false,
|
||||
size = 30,
|
||||
weight = 700,
|
||||
blursize = 0,
|
||||
scanlines = 0,
|
||||
antialias = true,
|
||||
underline = false,
|
||||
italic = false,
|
||||
strikeout = false,
|
||||
symbol = false,
|
||||
rotary = false,
|
||||
shadow = false,
|
||||
additive = false,
|
||||
outline = true,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "sf_tool_small", {
|
||||
font = "Verdana",
|
||||
size = 17,
|
||||
weight = 1000
|
||||
} )
|
||||
2149
lua/stormfox2/lib/cl_vgui.lua
Normal file
2149
lua/stormfox2/lib/cl_vgui.lua
Normal file
File diff suppressed because it is too large
Load Diff
538
lua/stormfox2/lib/sh_cami.lua
Normal file
538
lua/stormfox2/lib/sh_cami.lua
Normal file
@@ -0,0 +1,538 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
CAMI - Common Admin Mod Interface.
|
||||
Makes admin mods intercompatible and provides an abstract privilege interface
|
||||
for third party addons.
|
||||
|
||||
IMPORTANT: This is a draft script. It is very much WIP.
|
||||
|
||||
Follows the specification on this page:
|
||||
https://docs.google.com/document/d/1QIRVcAgZfAYf1aBl_dNV_ewR6P25wze2KmUVzlbFgMI
|
||||
|
||||
|
||||
Structures:
|
||||
CAMI_USERGROUP, defines the charactaristics of a usergroup:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the usergroup
|
||||
Inherits
|
||||
string
|
||||
The name of the usergroup this usergroup inherits from
|
||||
}
|
||||
|
||||
CAMI_PRIVILEGE, defines the charactaristics of a privilege:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the privilege
|
||||
MinAccess
|
||||
string
|
||||
One of the following three: user/admin/superadmin
|
||||
Description
|
||||
string
|
||||
optional
|
||||
A text describing the purpose of the privilege
|
||||
HasAccess
|
||||
function(
|
||||
privilege :: CAMI_PRIVILEGE,
|
||||
actor :: Player,
|
||||
target :: Player
|
||||
) :: bool
|
||||
optional
|
||||
Function that decides whether a player can execute this privilege,
|
||||
optionally on another player (target).
|
||||
}
|
||||
]]
|
||||
|
||||
-- Version number in YearMonthDay format.
|
||||
local version = 20150902.1
|
||||
|
||||
if CAMI and CAMI.Version >= version then return end
|
||||
|
||||
CAMI = CAMI or {}
|
||||
CAMI.Version = version
|
||||
|
||||
--[[
|
||||
usergroups
|
||||
Contains the registered CAMI_USERGROUP usergroup structures.
|
||||
Indexed by usergroup name.
|
||||
]]
|
||||
local usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {
|
||||
user = {
|
||||
Name = "user",
|
||||
Inherits = "user"
|
||||
},
|
||||
admin = {
|
||||
Name = "admin",
|
||||
Inherits = "user"
|
||||
},
|
||||
superadmin = {
|
||||
Name = "superadmin",
|
||||
Inherits = "admin"
|
||||
}
|
||||
}
|
||||
|
||||
--[[
|
||||
privileges
|
||||
Contains the registered CAMI_PRIVILEGE privilege structures.
|
||||
Indexed by privilege name.
|
||||
]]
|
||||
local privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}
|
||||
|
||||
--[[
|
||||
CAMI.RegisterUsergroup
|
||||
Registers a usergroup with CAMI.
|
||||
|
||||
Parameters:
|
||||
usergroup
|
||||
CAMI_USERGROUP
|
||||
(see CAMI_USERGROUP structure)
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.RegisterUsergroup function and the
|
||||
CAMI.OnUsergroupRegistered hook don't cause an infinite loop
|
||||
|
||||
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
The usergroup given as argument.
|
||||
]]
|
||||
function CAMI.RegisterUsergroup(usergroup, source)
|
||||
usergroups[usergroup.Name] = usergroup
|
||||
|
||||
hook.Call("CAMI.OnUsergroupRegistered", nil, usergroup, source)
|
||||
return usergroup
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterUsergroup
|
||||
Unregisters a usergroup from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the usergroup is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.UnregisterUsergroup function and the
|
||||
CAMI.OnUsergroupUnregistered hook don't cause an infinite loop
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterUsergroup(usergroupName, source)
|
||||
if not usergroups[usergroupName] then return false end
|
||||
|
||||
local usergroup = usergroups[usergroupName]
|
||||
usergroups[usergroupName] = nil
|
||||
|
||||
hook.Call("CAMI.OnUsergroupUnregistered", nil, usergroup, source)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroups
|
||||
Retrieves all registered usergroups.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_USERGROUP, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetUsergroups()
|
||||
return usergroups
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroup
|
||||
Receives information about a usergroup.
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
Returns nil when the usergroup does not exist.
|
||||
]]
|
||||
function CAMI.GetUsergroup(usergroupName)
|
||||
return usergroups[usergroupName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UsergroupInherits
|
||||
Returns true when usergroupName1 inherits usergroupName2.
|
||||
Note that usergroupName1 does not need to be a direct child.
|
||||
Every usergroup trivially inherits itself.
|
||||
|
||||
Parameters:
|
||||
usergroupName1
|
||||
string
|
||||
The name of the usergroup that is queried.
|
||||
usergroupName2
|
||||
string
|
||||
The name of the usergroup of which is queried whether usergroupName1
|
||||
inherits from.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether usergroupName1 inherits usergroupName2.
|
||||
]]
|
||||
function CAMI.UsergroupInherits(usergroupName1, usergroupName2)
|
||||
repeat
|
||||
if usergroupName1 == usergroupName2 then return true end
|
||||
|
||||
usergroupName1 = usergroups[usergroupName1] and
|
||||
usergroups[usergroupName1].Inherits or
|
||||
usergroupName1
|
||||
until not usergroups[usergroupName1] or
|
||||
usergroups[usergroupName1].Inherits == usergroupName1
|
||||
|
||||
-- One can only be sure the usergroup inherits from user if the
|
||||
-- usergroup isn't registered.
|
||||
return usergroupName1 == usergroupName2 or usergroupName2 == "user"
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.InheritanceRoot
|
||||
All usergroups must eventually inherit either user, admin or superadmin.
|
||||
Regardless of what inheritance mechism an admin may or may not have, this
|
||||
always applies.
|
||||
|
||||
This method always returns either user, admin or superadmin, based on what
|
||||
usergroups eventually inherit.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup of which the root of inheritance is
|
||||
requested
|
||||
|
||||
Return value:
|
||||
string
|
||||
The name of the root usergroup (either user, admin or superadmin)
|
||||
]]
|
||||
function CAMI.InheritanceRoot(usergroupName)
|
||||
if not usergroups[usergroupName] then return end
|
||||
|
||||
local inherits = usergroups[usergroupName].Inherits
|
||||
while inherits ~= usergroups[usergroupName].Inherits do
|
||||
usergroupName = usergroups[usergroupName].Inherits
|
||||
end
|
||||
|
||||
return usergroupName
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.RegisterPrivilege
|
||||
Registers a privilege with CAMI.
|
||||
Note: do NOT register all your admin mod's privileges with this function!
|
||||
This function is for third party addons to register privileges
|
||||
with admin mods, not for admin mods sharing the privileges amongst one
|
||||
another.
|
||||
|
||||
Parameters:
|
||||
privilege
|
||||
CAMI_PRIVILEGE
|
||||
See CAMI_PRIVILEGE structure.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE
|
||||
The privilege given as argument.
|
||||
]]
|
||||
function CAMI.RegisterPrivilege(privilege)
|
||||
privileges[privilege.Name] = privilege
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeRegistered", nil, privilege)
|
||||
|
||||
return privilege
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterPrivilege
|
||||
Unregisters a privilege from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the privilege is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterPrivilege(privilegeName)
|
||||
if not privileges[privilegeName] then return false end
|
||||
|
||||
local privilege = privileges[privilegeName]
|
||||
privileges[privilegeName] = nil
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeUnregistered", nil, privilege)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivileges
|
||||
Retrieves all registered privileges.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_PRIVILEGE, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetPrivileges()
|
||||
return privileges
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivilege
|
||||
Receives information about a privilege.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE when the privilege exists.
|
||||
nil when the privilege does not exist.
|
||||
]]
|
||||
function CAMI.GetPrivilege(privilegeName)
|
||||
return privileges[privilegeName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.PlayerHasAccess
|
||||
Queries whether a certain player has the right to perform a certain action.
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorPly
|
||||
Player
|
||||
The player of which is requested whether they have the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
-- Default access handler
|
||||
local defaultAccessHandler = {["CAMI.PlayerHasAccess"] =
|
||||
function(_, actorPly, privilegeName, callback, _, extraInfoTbl)
|
||||
-- The server always has access in the fallback
|
||||
if not IsValid(actorPly) then return callback(true, "Fallback.") end
|
||||
|
||||
local priv = privileges[privilegeName]
|
||||
|
||||
local fallback = extraInfoTbl and (
|
||||
not extraInfoTbl.Fallback and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "user" and true or
|
||||
extraInfoTbl.Fallback == "admin" and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "superadmin" and actorPly:IsSuperAdmin())
|
||||
|
||||
|
||||
if not priv then return callback(fallback, "Fallback.") end
|
||||
|
||||
callback(
|
||||
priv.MinAccess == "user" or
|
||||
priv.MinAccess == "admin" and actorPly:IsAdmin() or
|
||||
priv.MinAccess == "superadmin" and actorPly:IsSuperAdmin()
|
||||
, "Fallback.")
|
||||
end,
|
||||
["CAMI.SteamIDHasAccess"] =
|
||||
function(_, _, _, callback)
|
||||
callback(false, "No information available.")
|
||||
end
|
||||
}
|
||||
function CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
hook.Call("CAMI.PlayerHasAccess", defaultAccessHandler, actorPly,
|
||||
privilegeName, callback, targetPly, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPlayersWithAccess
|
||||
Finds the list of currently joined players who have the right to perform a
|
||||
certain action.
|
||||
NOTE: this function will NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(players)
|
||||
This function will be called with the list of players with access.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
]]
|
||||
function CAMI.GetPlayersWithAccess(privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
local allowedPlys = {}
|
||||
local allPlys = player.GetAll()
|
||||
local countdown = #allPlys
|
||||
|
||||
local function onResult(ply, hasAccess, _)
|
||||
countdown = countdown - 1
|
||||
|
||||
if hasAccess then table.insert(allowedPlys, ply) end
|
||||
if countdown == 0 then callback(allowedPlys) end
|
||||
end
|
||||
|
||||
for _, ply in pairs(allPlys) do
|
||||
CAMI.PlayerHasAccess(ply, privilegeName,
|
||||
function(...) onResult(ply, ...) end,
|
||||
targetPly, extraInfoTbl)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SteamIDHasAccess
|
||||
Queries whether a player with a steam ID has the right to perform a certain
|
||||
action.
|
||||
Note: the player does not need to be in the server for this to
|
||||
work.
|
||||
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorSteam
|
||||
Player
|
||||
The SteamID of the player of which is requested whether they have
|
||||
the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetSteam
|
||||
Optional.
|
||||
The SteamID of the player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
function CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,
|
||||
targetSteam, extraInfoTbl)
|
||||
hook.Call("CAMI.SteamIDHasAccess", defaultAccessHandler, actorSteam,
|
||||
privilegeName, callback, targetSteam, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a player. This
|
||||
function communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
Player
|
||||
The player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalUserGroupChanged(ply, old, new, source)
|
||||
hook.Call("CAMI.PlayerUsergroupChanged", nil, ply, old, new, source)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalSteamIDUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a disconnected
|
||||
player. This communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
string
|
||||
The steam ID of the player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalSteamIDUserGroupChanged(steamId, old, new, source)
|
||||
hook.Call("CAMI.SteamIDUsergroupChanged", nil, steamId, old, new, source)
|
||||
end
|
||||
228
lua/stormfox2/lib/sh_data.lua
Normal file
228
lua/stormfox2/lib/sh_data.lua
Normal file
@@ -0,0 +1,228 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Unlike SF1, this doesn't support networking.
|
||||
|
||||
If timespeed changes, and some lerpvalues like temperature has been applied, we need to keep it syncronised.
|
||||
That is why we now use Time value instead of CurTime
|
||||
|
||||
Data.Set( sKey, zVar, nDelta ) Sets the data. Supports lerping if given delta.
|
||||
Data.Get( sKey, zDefault ) Returns the data. Returns zDefault if nil.
|
||||
Data.GetFinal( zKey, zDefault) Returns the data without calculating the lerp.
|
||||
Data.IsLerping( sKey ) Returns true if the data is currently lerping.
|
||||
|
||||
Hooks:
|
||||
- StormFox2.data.change sKey zVar Called when data changed or started lerping.
|
||||
- StormFox2.data.lerpstart sKey zVar Called when data started lerping
|
||||
- StormFox2.data.lerpend sKey zVar Called when data stopped lerping (This will only be called if we check for the variable)
|
||||
]]
|
||||
StormFox2.Data = {}
|
||||
|
||||
StormFox_DATA = {} -- Var
|
||||
StormFox_AIMDATA = {} -- Var, start, end
|
||||
|
||||
--[[TODO: There are still problems with nil varables.
|
||||
]]
|
||||
|
||||
---Returns the final data, ignoring lerp. Will return zDefault as fallback.
|
||||
---@param sKey string
|
||||
---@param zDefault any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Data.GetFinal( sKey, zDefault )
|
||||
if StormFox_AIMDATA[sKey] then
|
||||
if StormFox_AIMDATA[sKey][1] ~= nil then
|
||||
return StormFox_AIMDATA[sKey][1]
|
||||
else
|
||||
return zDefault
|
||||
end
|
||||
end
|
||||
if StormFox_DATA[sKey] ~= nil then
|
||||
return StormFox_DATA[sKey]
|
||||
end
|
||||
return zDefault
|
||||
end
|
||||
|
||||
local lerpCache = {}
|
||||
local function calcFraction(start_cur, end_cur)
|
||||
local n = CurTime()
|
||||
if n >= end_cur then return 1 end
|
||||
local d = end_cur - start_cur
|
||||
return (n - start_cur) / d
|
||||
end
|
||||
do
|
||||
local function isColor(t)
|
||||
if type(t) ~= "table" then return false end
|
||||
return t.r and t.g and t.b and true or false
|
||||
end
|
||||
local function LerpVar(fraction, from, to)
|
||||
local t = type(from)
|
||||
if t ~= type(to) then
|
||||
--StormFox2.Warning("Can't lerp " .. type(from) .. " to " .. type(to) .. "!")
|
||||
return to
|
||||
end
|
||||
if t == "number" then
|
||||
return Lerp(fraction, from, to)
|
||||
elseif t == "string" then
|
||||
return fraction > .5 and to or from
|
||||
elseif isColor(from) then
|
||||
local r = Lerp(fraction, from.r, to.r)
|
||||
local g = Lerp(fraction, from.g, to.g)
|
||||
local b = Lerp(fraction, from.b, to.b)
|
||||
local a = Lerp(fraction, from.a, to.a)
|
||||
return Color(r,g,b,a)
|
||||
elseif t == "vector" then
|
||||
return LerpVector(fraction, from, to)
|
||||
elseif t == "angle" then
|
||||
return LerpAngle(fraction, from, to)
|
||||
elseif t == "boolean" then
|
||||
if fraction > .5 then
|
||||
return to
|
||||
else
|
||||
return from
|
||||
end
|
||||
else
|
||||
--print("UNKNOWN", t,"TO",to)
|
||||
end
|
||||
end
|
||||
|
||||
---Returns data. Will return zDefault as fallback.
|
||||
---@param sKey string
|
||||
---@param zDefault any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Data.Get( sKey, zDefault )
|
||||
-- Check if lerping
|
||||
local var1 = StormFox_DATA[sKey]
|
||||
if not StormFox_AIMDATA[sKey] then
|
||||
if var1 ~= nil then
|
||||
return var1
|
||||
else
|
||||
return zDefault
|
||||
end
|
||||
end
|
||||
-- Check cache and return
|
||||
if lerpCache[sKey] ~= nil then return lerpCache[sKey] end
|
||||
-- Calc
|
||||
local fraction = calcFraction(StormFox_AIMDATA[sKey][2],StormFox_AIMDATA[sKey][3])
|
||||
local var2 = StormFox_AIMDATA[sKey][1]
|
||||
if fraction <= 0 then
|
||||
return var1
|
||||
elseif fraction < 1 then
|
||||
lerpCache[sKey] = LerpVar( fraction, var1, var2 )
|
||||
if not lerpCache[sKey] then
|
||||
--print("DATA",sKey, zDefault)
|
||||
--print(debug.traceback())
|
||||
end
|
||||
return lerpCache[sKey] or zDefault
|
||||
else -- Fraction end
|
||||
StormFox_DATA[sKey] = var2
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
hook.Run("StormFox2.data.lerpend",sKey,var2)
|
||||
return var2 or zDefault
|
||||
end
|
||||
end
|
||||
local n = 0
|
||||
-- Reset cache after 4 frames
|
||||
hook.Add("Think", "StormFox2.resetdatalerp", function()
|
||||
n = n + 1
|
||||
if n < 4 then return end
|
||||
n = 0
|
||||
lerpCache = {}
|
||||
end)
|
||||
end
|
||||
|
||||
---Sets data. Will lerp if given delta time. Use StormFox2.Network.Set if you want want to network it.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@shared
|
||||
function StormFox2.Data.Set( sKey, zVar, nDelta )
|
||||
-- Check if vars are the same
|
||||
if StormFox_DATA[sKey] ~= nil and not StormFox_AIMDATA[sKey] then
|
||||
if StormFox_DATA[sKey] == zVar then return end
|
||||
end
|
||||
-- If time is paused, there shouldn't be any lerping
|
||||
if StormFox2.Time and StormFox2.Time.IsPaused and StormFox2.Time.IsPaused() then
|
||||
nDelta = 0
|
||||
end
|
||||
-- Delete old cache
|
||||
lerpCache[sKey] = nil
|
||||
-- Set to nil
|
||||
if not zVar and zVar == nil then
|
||||
StormFox_DATA[sKey] = nil
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
return
|
||||
end
|
||||
-- If delta is 0 or below. (Or no prev data). Set it.
|
||||
if not nDelta or nDelta <= 0 or StormFox_DATA[sKey] == nil or StormFox2.Time.GetSpeed_RAW() <= 0 then
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
StormFox_DATA[sKey] = zVar
|
||||
hook.Run("StormFox2.data.change",sKey,zVar)
|
||||
return
|
||||
end
|
||||
-- Get the current lerping value and set that as a start
|
||||
if StormFox_AIMDATA[sKey] then
|
||||
StormFox_DATA[sKey] = StormFox2.Data.Get( sKey )
|
||||
end
|
||||
StormFox_AIMDATA[sKey] = {zVar, CurTime(), CurTime() + nDelta, StormFox2.Time.GetSpeed_RAW()}
|
||||
hook.Run("StormFox2.data.lerpstart",sKey,zVar, nDelta)
|
||||
hook.Run("StormFox2.data.change", sKey, zVar, nDelta)
|
||||
end
|
||||
|
||||
---Returns true if the value is currently lerping.
|
||||
---@param sKey string
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Data.IsLerping( sKey )
|
||||
if not StormFox_AIMDATA[sKey] then return false end
|
||||
-- Check and see if we're done lerping
|
||||
local fraction = calcFraction(StormFox_AIMDATA[sKey][2],StormFox_AIMDATA[sKey][3])
|
||||
if fraction < 1 then
|
||||
return true
|
||||
end
|
||||
-- We're done lerping.
|
||||
StormFox_DATA[sKey] = StormFox2.Data.GetFinal( sKey )
|
||||
StormFox_AIMDATA[sKey] = nil
|
||||
lerpCache[sKey] = nil
|
||||
hook.Run("StormFox2.data.lerpend",sKey,zVar)
|
||||
return true
|
||||
end
|
||||
|
||||
---Returns a CurTime for when the data is done lerping.
|
||||
---@param sKey string
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Data.GetLerpEnd( sKey )
|
||||
if not StormFox_AIMDATA[sKey] then return 0 end
|
||||
return StormFox_AIMDATA[sKey][3]
|
||||
end
|
||||
|
||||
-- If time changes, we need to update the lerp values
|
||||
hook.Add("StormFox2.Time.Changed", "StormFox2.datatimefix", function()
|
||||
local nT = StormFox2.Time.GetSpeed_RAW()
|
||||
local c = CurTime()
|
||||
if nT <= 0.001 then return end
|
||||
for k,v in pairs( StormFox_AIMDATA ) do
|
||||
if not v[4] or v[4] == nT then continue end
|
||||
local now_value = StormFox2.Data.Get( k )
|
||||
if not StormFox_AIMDATA[k] then continue end -- After checking the value, it is now gone.
|
||||
if now_value then
|
||||
StormFox_DATA[k] = now_value
|
||||
end
|
||||
local delta_timeamount = (v[3] - c) -- Time left
|
||||
local delta_time = v[4] / nT -- Time multiplication
|
||||
StormFox_AIMDATA[k][2] = c
|
||||
StormFox_AIMDATA[k][3] = c + delta_timeamount * delta_time
|
||||
StormFox_AIMDATA[k][4] = nT
|
||||
end
|
||||
end)
|
||||
1006
lua/stormfox2/lib/sh_downfall.lua
Normal file
1006
lua/stormfox2/lib/sh_downfall.lua
Normal file
File diff suppressed because it is too large
Load Diff
1040
lua/stormfox2/lib/sh_mapglass.lua
Normal file
1040
lua/stormfox2/lib/sh_mapglass.lua
Normal file
File diff suppressed because it is too large
Load Diff
90
lua/stormfox2/lib/sh_network.lua
Normal file
90
lua/stormfox2/lib/sh_network.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Network.Set( sKey, zVar, nDelta ) Same as Data.Set( sKey, zVar, nDelta ) but networks it to all clients.
|
||||
]]
|
||||
StormFox2.Network = {}
|
||||
StormFox_NETWORK = {} -- Var
|
||||
|
||||
if SERVER then
|
||||
local tickets = {}
|
||||
-- Forces a client to recive the data
|
||||
function StormFox2.Network.ForceUpdate( ply )
|
||||
tickets[ply] = true
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(false)
|
||||
net.WriteTable(StormFox_NETWORK)
|
||||
net.Send(ply)
|
||||
hook.Run("StormFox2.data.initspawn", ply)
|
||||
end
|
||||
|
||||
---Same as StormFox2.Data.Set, but networks it to all clients.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@server
|
||||
function StormFox2.Network.Set( sKey, zVar, nDelta )
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
if StormFox_NETWORK[sKey] == zVar then return end
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(true)
|
||||
net.WriteString(sKey)
|
||||
net.WriteType(zVar)
|
||||
net.WriteUInt(nDelta or 0, 16)
|
||||
net.Broadcast()
|
||||
StormFox_NETWORK[sKey] = zVar
|
||||
end
|
||||
|
||||
---Force-set the data, ignoring cache.
|
||||
---@param sKey string
|
||||
---@param zVar any
|
||||
---@param nDelta any
|
||||
---@server
|
||||
function StormFox2.Network.ForceSet( sKey, zVar, nDelta )
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(true)
|
||||
net.WriteString(sKey)
|
||||
net.WriteType(zVar)
|
||||
net.WriteUInt(nDelta or 0, 16)
|
||||
net.Broadcast()
|
||||
StormFox_NETWORK[sKey] = zVar
|
||||
end
|
||||
net.Receive(StormFox2.Net.Network, function(len, ply)
|
||||
if tickets[ply] then return end
|
||||
tickets[ply] = true
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.WriteBool(false)
|
||||
net.WriteTable(StormFox_NETWORK)
|
||||
net.Send(ply)
|
||||
hook.Run("StormFox2.data.initspawn", ply)
|
||||
end)
|
||||
else
|
||||
net.Receive(StormFox2.Net.Network, function(len)
|
||||
if net.ReadBool() then
|
||||
local sKey = net.ReadString()
|
||||
local zVar = net.ReadType()
|
||||
local nDelta = net.ReadUInt(16)
|
||||
StormFox2.Data.Set(sKey, zVar, nDelta)
|
||||
else
|
||||
StormFox_NETWORK = net.ReadTable()
|
||||
for k,v in pairs(StormFox_NETWORK) do
|
||||
StormFox2.Data.Set(k, v)
|
||||
end
|
||||
end
|
||||
end)
|
||||
-- Ask the server what data we have
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.network", function()
|
||||
net.Start(StormFox2.Net.Network)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
164
lua/stormfox2/lib/sh_permission.lua
Normal file
164
lua/stormfox2/lib/sh_permission.lua
Normal file
@@ -0,0 +1,164 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
StormFox2.Permission = {}
|
||||
|
||||
hook.Add("stormfox2.postlib", "stormfox2.privileges", function()
|
||||
if not CAMI then return end
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "StormFox Settings",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
-- Permission to edit StormFox weather and time
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "StormFox WeatherEdit",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
end)
|
||||
|
||||
local SF_SERVEREDIT = 0
|
||||
local SF_WEATHEREDIT= 1
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("StormFox2.menu")
|
||||
-- "Fake" settings
|
||||
local commands = {
|
||||
["cvslist"] = function( var )
|
||||
StormFox2.Setting.SetCVS( tostring( var ) )
|
||||
end
|
||||
}
|
||||
net.Receive("StormFox2.menu", function(len, ply)
|
||||
local req = net.ReadBool()
|
||||
if ply:IsListenServerHost() or game.SinglePlayer() then
|
||||
net.Start("StormFox2.menu")
|
||||
net.WriteBool(req)
|
||||
net.Send( ply )
|
||||
return
|
||||
end
|
||||
CAMI.PlayerHasAccess(ply,req and "StormFox Settings" or "StormFox WeatherEdit",function(b)
|
||||
if not b then return end
|
||||
net.Start("StormFox2.menu")
|
||||
net.WriteBool(req)
|
||||
net.Send( ply )
|
||||
end)
|
||||
end)
|
||||
local function NoAccess(ply, msg)
|
||||
if not ply then
|
||||
MsgC( Color(155,155,255),"[StormFox2] ", color_white, msg )
|
||||
MsgN()
|
||||
return
|
||||
end
|
||||
net.Start( StormFox2.Net.Permission )
|
||||
net.WriteString(msg)
|
||||
net.Send(ply)
|
||||
end
|
||||
local function plyRequestSetting(ply, convar, var)
|
||||
if not CAMI then return end
|
||||
-- Check if its a stormfox setting
|
||||
local obj = StormFox2.Setting.GetObject( convar ) or commands[ convar ]
|
||||
if not obj then
|
||||
if ply then
|
||||
NoAccess(ply, "Invalid setting: " .. tostring(convar))
|
||||
end
|
||||
return false, "Not SF"
|
||||
end
|
||||
-- If singleplayer/host
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then
|
||||
if type(obj) == "function" then
|
||||
obj( var )
|
||||
else
|
||||
obj:SetValue( var )
|
||||
end
|
||||
return
|
||||
end
|
||||
-- Check CAMI
|
||||
CAMI.PlayerHasAccess(ply,"StormFox Settings",function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the settings!")
|
||||
return
|
||||
end
|
||||
if type(obj) == "function" then
|
||||
obj( var )
|
||||
else
|
||||
obj:SetValue( var )
|
||||
end
|
||||
end)
|
||||
end
|
||||
local function plyRequestEdit( ply, tID, var)
|
||||
if not CAMI then return end
|
||||
-- If singleplayer/host
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then
|
||||
return StormFox2.Menu.SetWeatherData(ply, tID, var)
|
||||
end
|
||||
-- Check CAMI
|
||||
CAMI.PlayerHasAccess(ply,"StormFox WeatherEdit",function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the weather!")
|
||||
return
|
||||
end
|
||||
StormFox2.Menu.SetWeatherData(ply, tID, var)
|
||||
end)
|
||||
end
|
||||
net.Receive( StormFox2.Net.Permission, function(len, ply)
|
||||
local t = net.ReadUInt(1)
|
||||
if t == SF_SERVEREDIT then
|
||||
plyRequestSetting(ply, net.ReadString(), net.ReadType())
|
||||
elseif t == SF_WEATHEREDIT then
|
||||
plyRequestEdit(ply, net.ReadUInt(4), net.ReadType())
|
||||
end
|
||||
end)
|
||||
|
||||
---Asks CAMI if the user has access to said permission. Will call and return onSuccess if they do.
|
||||
---@param ply Player
|
||||
---@param sPermission string
|
||||
---@param onSuccess function
|
||||
---@param ... any
|
||||
---@return any|nil
|
||||
---@server
|
||||
function StormFox2.Permission.EditAccess(ply, sPermission, onSuccess, ...)
|
||||
if not ply or ply:IsListenServerHost() then -- Console or host
|
||||
return onSuccess(ply, ... )
|
||||
end
|
||||
local a = {...}
|
||||
CAMI.PlayerHasAccess(ply,sPermission,function(b)
|
||||
if not b then
|
||||
NoAccess(ply, "You don't have access to edit the weather.")
|
||||
return
|
||||
end
|
||||
onSuccess(ply, unpack(a) )
|
||||
end)
|
||||
end
|
||||
else
|
||||
net.Receive(StormFox2.Net.Permission, function(len)
|
||||
local str = net.ReadString()
|
||||
chat.AddText(Color(155,155,255),"[StormFox2] ", color_white, str)
|
||||
end)
|
||||
net.Receive("StormFox2.menu", function(len)
|
||||
local n = net.ReadBool()
|
||||
if n then
|
||||
StormFox2.Menu._OpenSV()
|
||||
else
|
||||
StormFox2.Menu._OpenController()
|
||||
end
|
||||
end)
|
||||
|
||||
---Asks the server to change a setting.
|
||||
---@param convar string
|
||||
---@param var any
|
||||
---@client
|
||||
function StormFox2.Permission.RequestSetting( convar, var )
|
||||
net.Start(StormFox2.Net.Permission)
|
||||
net.WriteUInt(SF_SERVEREDIT, 1)
|
||||
net.WriteString( convar )
|
||||
net.WriteType(var)
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
22
lua/stormfox2/lib/sh_reset.lua
Normal file
22
lua/stormfox2/lib/sh_reset.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Allows to reset
|
||||
if _STORMFOX_POSTENTITY then
|
||||
timer.Simple(2, function()
|
||||
hook.Run("StormFox2.InitPostEntity")
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("InitPostEntity", "SF_PostEntity", function()
|
||||
hook.Run("StormFox2.InitPostEntity")
|
||||
_STORMFOX_POSTENTITY = true
|
||||
end)
|
||||
815
lua/stormfox2/lib/sh_settings.lua
Normal file
815
lua/stormfox2/lib/sh_settings.lua
Normal file
@@ -0,0 +1,815 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
StormFox Settings
|
||||
Handle settings and convert convars.
|
||||
|
||||
- Hooks: StormFox2.Setting.Change sName, vVarable
|
||||
---------------------------------------------------------------------------]]
|
||||
StormFox2.Setting = {}
|
||||
-- Local functions and var
|
||||
|
||||
local NET_ALLSETTINGS = 0
|
||||
local NET_UPDATE = 1
|
||||
|
||||
local settingCallback = {}
|
||||
local settings = {}
|
||||
local cache = {}
|
||||
|
||||
local ValueToString, StringToValue
|
||||
do
|
||||
function StringToValue( str, _type )
|
||||
_type = _type:lower()
|
||||
if ( _type == "vector" ) then return Vector( str ) end
|
||||
if ( _type == "angle" ) then return Angle( str ) end
|
||||
if ( _type == "float" or _type == "number" ) then return tonumber( str ) end
|
||||
if ( _type == "int" ) then return math.Round( tonumber( str ) ) end
|
||||
if ( _type == "bool" or _type == "boolean" ) then return tobool( str ) end
|
||||
if ( _type == "string" ) then return tostring( str ) end
|
||||
if ( _type == "entity" ) then return Entity( str ) end
|
||||
StormFox2.Warning("Unable parse: " .. _type, true)
|
||||
end
|
||||
function ValueToString( var, _type )
|
||||
_type = (_type or type( var )):lower()
|
||||
if _type == "vector" or _type == "angle" then return string.format( "%.2f %.2f %.2f", var:Unpack() ) end
|
||||
if _type == "number" or _type == "float" then return util.NiceFloat( var ) end
|
||||
if _type == "int" then var = math.Round( var ) end
|
||||
return tostring( var )
|
||||
end
|
||||
end
|
||||
-- Load the settings
|
||||
local mapFile, defaultFile
|
||||
if SERVER then
|
||||
mapFile, defaultFile = "stormfox2/sv_settings/" .. game.GetMap() .. ".json", "stormfox2/sv_settings/default.json"
|
||||
else
|
||||
mapFile, defaultFile = "stormfox2/cl_settings/" .. game.GetMap() .. ".json", "stormfox2/cl_settings/default.json"
|
||||
end
|
||||
local settingsFile = file.Exists(mapFile, "DATA") and mapFile or defaultFile
|
||||
local fileData = {}
|
||||
if file.Exists(settingsFile, "DATA") then
|
||||
fileData = util.JSONToTable( file.Read(settingsFile, "DATA") or "" ) or {}
|
||||
end
|
||||
local blockSaveFile = false
|
||||
local function saveToFile()
|
||||
if blockSaveFile then return end
|
||||
local data = {}
|
||||
for sName, obj in pairs( settings ) do
|
||||
if CLIENT and obj:IsServer() then continue end -- If you're the client, ignore server settings
|
||||
if sName == "mapfile" then continue end
|
||||
if sName == "mapfile_cl" then continue end
|
||||
if obj:IsDefault() then continue end
|
||||
data[sName] = obj:GetString()
|
||||
end
|
||||
StormFox2.FileWrite( settingsFile, util.TableToJSON(data, true) )
|
||||
end
|
||||
|
||||
---Returns faæse if we're saving to the default json file.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.IsUsingMapFile()
|
||||
return settingsFile == mapFile
|
||||
end
|
||||
|
||||
---Enable/Disable saving to map-specific file.
|
||||
---@param bool boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.UseMapFile( bool )
|
||||
if bool then
|
||||
if settingsFile == mapFile then return end
|
||||
settingsFile = mapFile
|
||||
-- Settings only save once callback is done. Therefor we force this to save
|
||||
local ob = blockSaveFile
|
||||
blockSaveFile = false
|
||||
saveToFile() -- "Copy" the settings to the file
|
||||
blockSaveFile = ob
|
||||
else
|
||||
if settingsFile == defaultFile then return end
|
||||
file.Delete(mapFile)
|
||||
settingsFile = defaultFile
|
||||
-- Reload the default file
|
||||
fileData = util.JSONToTable( file.Read(settingsFile, "DATA") or "" ) or {}
|
||||
blockSaveFile = true
|
||||
for sName, var in pairs( fileData ) do
|
||||
local obj = StormFox2.Setting.GetObject( sName )
|
||||
if not obj then continue end
|
||||
local newVar = fileData[sName] and StringToValue(fileData[sName], obj.type)
|
||||
obj:SetValue( newVar )
|
||||
end
|
||||
blockSaveFile = false
|
||||
end
|
||||
end
|
||||
|
||||
---Forces SF2 to save the settings.
|
||||
---@shared
|
||||
function StormFox2.Setting.ForceSave()
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
end
|
||||
|
||||
---Returns the file we're saving to.
|
||||
---@return string
|
||||
function StormFox2.Setting.GetSaveFile()
|
||||
return settingsFile
|
||||
end
|
||||
|
||||
-- Meta Table
|
||||
|
||||
---@class SF2Convar
|
||||
---@field SetGroup function
|
||||
---@field GetGroup function
|
||||
---@field SetDescription function
|
||||
---@field GetDescription function
|
||||
local meta = {}
|
||||
meta.__index = meta
|
||||
AccessorFunc(meta, "group", "Group")
|
||||
AccessorFunc(meta, "desc", "Description")
|
||||
function meta:GetName()
|
||||
return self.sName
|
||||
end
|
||||
function meta:GetValue()
|
||||
return self.value
|
||||
end
|
||||
function meta:GetDescription()
|
||||
return self.desc
|
||||
end
|
||||
function meta:IsSecret()
|
||||
return self.isSecret or false
|
||||
end
|
||||
function meta:SetValue( var )
|
||||
if self:GetValue() == var then return self end -- Ignore
|
||||
StormFox2.Setting.Set(self:GetName(), var)
|
||||
return self
|
||||
end
|
||||
function meta:GetDefault()
|
||||
return self.default
|
||||
end
|
||||
function meta:IsDefault()
|
||||
return self:GetValue() == self:GetDefault()
|
||||
end
|
||||
function meta:Revert()
|
||||
self:SetValue( self:GetDefault() )
|
||||
end
|
||||
function meta:IsServer()
|
||||
return self.server
|
||||
end
|
||||
function meta:GetType()
|
||||
return self.type
|
||||
end
|
||||
function meta:SetMenuType( sType, tSortOrter )
|
||||
StormFox2.Setting.SetType( self:GetName(), sType, tSortOrter )
|
||||
return self
|
||||
end
|
||||
function meta:GetMin()
|
||||
return self.min
|
||||
end
|
||||
function meta:GetMax()
|
||||
return self.max
|
||||
end
|
||||
function meta:GetString()
|
||||
return ValueToString(self:GetValue(), self:GetType())
|
||||
end
|
||||
function meta:IsFuzzyOn()
|
||||
if not self:GetValue() then return false end
|
||||
if self.type == "number" then
|
||||
if self:GetValue() <= ( self:GetMin() or 0 ) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
function meta:SetFuzzyOn()
|
||||
if self.type == "boolean" then
|
||||
self:SetValue( true )
|
||||
elseif self.type == "number" then
|
||||
local lowest = ( self:GetMin() or 0 )
|
||||
self:SetValue( lowest + 1 )
|
||||
end
|
||||
return self
|
||||
end
|
||||
function meta:SetFuzzyOff()
|
||||
if self.type == "boolean" then
|
||||
self:SetValue( false )
|
||||
elseif self.type == "number" then
|
||||
local lowest = ( self:GetMin() or 0 )
|
||||
self:SetValue( lowest )
|
||||
end
|
||||
return self
|
||||
end
|
||||
function meta:SetFromString( str, type )
|
||||
self:SetValue( StringToValue( str, type or self.type ) )
|
||||
return self
|
||||
end
|
||||
function meta:AddCallback(fFunc,sID)
|
||||
StormFox2.Setting.Callback(self:GetName(),fFunc,sID)
|
||||
end
|
||||
-- Ties the setting to others. Does require all to be booleans or form of toggles
|
||||
do
|
||||
local radioTab = {}
|
||||
local radioTabDefault = {}
|
||||
local blockLoop = false
|
||||
local function callBack(vVar, oldVar, sName, id)
|
||||
if blockLoop then return end -- Another setting made you change. Don't run.
|
||||
local obj = settings[sName]
|
||||
if not obj then StormFox2.Warning("Invalid radio-setting!", true) end
|
||||
local a = radioTab[sName]
|
||||
if not a then return end
|
||||
-- Make sure we turned "on"
|
||||
if not obj:IsFuzzyOn() then -- We got turned off. Make sure at least one is on
|
||||
if not blockLoop then -- Turned off, and we didn't get called by others
|
||||
local default = a[1] -- First one in list. This is to ensure self never get set.
|
||||
for _, other in ipairs( a ) do
|
||||
if other:IsFuzzyOn() then return end -- One of the others are on. Ignore.
|
||||
if radioTabDefault[other:GetName()] then -- I'm the default one
|
||||
default = other
|
||||
end
|
||||
end
|
||||
-- All other settings are off. Try and switch the default on.
|
||||
if default:GetName() == sName then -- Tell the settings we can't be turned off
|
||||
return false
|
||||
else
|
||||
default:SetFuzzyOn()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
blockLoop = true
|
||||
-- Tell the others we turned on, and they have to turn off
|
||||
for _, other in ipairs( a ) do
|
||||
if other:GetName() == obj:GetName() then continue end -- Just to make sure we don't loop around
|
||||
other:SetFuzzyOff()
|
||||
end
|
||||
blockLoop = false
|
||||
end
|
||||
local callOthers = true
|
||||
-- Makes all settings turn off, if one of them are turned on.
|
||||
function meta:SetRadioAll( ... )
|
||||
if self:IsServer() and CLIENT then -- Not your job to keep track.
|
||||
self._radioB = true
|
||||
for _, other in ipairs( { ... } ) do
|
||||
other._radioB = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
local a = {}
|
||||
-- Make sure the arguments doesn't contain itself and all is from the same realm.
|
||||
local f = { ... }
|
||||
for _, other in ipairs( f ) do
|
||||
if other:GetName() == self:GetName() then continue end -- Don't include self
|
||||
if other:IsServer() ~= self:IsServer() then -- Make sure same realm
|
||||
StormFox2.Warning(other:GetName() .. " tried to tie itself to a setting from another realm!")
|
||||
continue
|
||||
end
|
||||
table.insert(a, other)
|
||||
end
|
||||
if #a < 1 then StormFox2.Warning(self:GetName() .. " tried to tie itself to nothing!",true) end
|
||||
-- Tell the other settings to do the same
|
||||
if callOthers then
|
||||
callOthers = false
|
||||
for _, other in ipairs( a ) do
|
||||
other:SetRadio( self, unpack( a ) )
|
||||
end
|
||||
callOthers = true
|
||||
end
|
||||
radioTab[self:GetName()] = a
|
||||
StormFox2.Setting.Callback(self:GetName(),callBack,"radio_setting")
|
||||
return self
|
||||
end
|
||||
function meta:SetRadioDefault() -- Turn on if all others are off
|
||||
radioTabDefault[self:GetName()] = true
|
||||
return self
|
||||
end
|
||||
function meta:IsRadio()
|
||||
return (radioTab[self:GetName()] or self._radioB) and true or false
|
||||
end
|
||||
-- Tells these settings to turn off, if this setting is turned on
|
||||
function meta:SetRadio( ... )
|
||||
if self:IsServer() and CLIENT then -- Not your job to keep track.
|
||||
self._radioB = true
|
||||
return self
|
||||
end
|
||||
local a = {}
|
||||
-- Make sure the arguments doesn't contain itself and all is from the same realm.
|
||||
for _, other in ipairs( { ... } ) do
|
||||
if other:GetName() == self:GetName() then continue end -- Don't include self
|
||||
if other:IsServer() ~= self:IsServer() then -- Make sure same realm
|
||||
StormFox2.Warning(other:GetName() .. " tried to tie itself to a setting from another realm!")
|
||||
continue
|
||||
end
|
||||
table.insert(a, other)
|
||||
end
|
||||
if #a < 1 then StormFox2.Warning(self:GetName() .. " tried to tie itself to nothing!",true) end
|
||||
radioTab[self:GetName()] = a
|
||||
StormFox2.Setting.Callback(self:GetName(),callBack,"radio_setting")
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
local postSettingChace = {}
|
||||
-- Creates a setting and returns the setting-object
|
||||
|
||||
---Creates a server-side setting. Has to be called on the client to show up in the menu.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@param sDescription? string
|
||||
---@param sGroup? string
|
||||
---@param nMin? number
|
||||
---@param nMax? number
|
||||
---@return SF2Convar
|
||||
---@shared
|
||||
function StormFox2.Setting.AddSV(sName,vDefaultVar,sDescription,sGroup, nMin, nMax)
|
||||
if settings[sName] then return settings[sName] end -- Already created
|
||||
if StormFox2.Map then
|
||||
vDefaultVar = StormFox2.Map.GetSetting( sName ) or vDefaultVar
|
||||
end
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t.sName = sName
|
||||
t.type = type(vDefaultVar)
|
||||
if SERVER then
|
||||
if fileData[sName] ~= nil then
|
||||
t.value = StringToValue(fileData[sName], t.type)
|
||||
end
|
||||
if t.value == nil then -- Check convar before setting the setting.
|
||||
local con = GetConVar("sf_" .. sName)
|
||||
if con then
|
||||
t.value = StringToValue(con:GetString(), t.type)
|
||||
end
|
||||
end
|
||||
if t.value == nil then -- If all fails, use the default
|
||||
t.value = vDefaultVar
|
||||
end
|
||||
else
|
||||
t.value = postSettingChace[sName] or vDefaultVar
|
||||
end
|
||||
t.default = vDefaultVar
|
||||
t.server = true
|
||||
t.min = nMin
|
||||
t.max = nMax
|
||||
t:SetGroup( sGroup )
|
||||
t:SetDescription( sDescription )
|
||||
settings[sName] = t
|
||||
return t
|
||||
end
|
||||
|
||||
-- Creates a setting and returns the setting-object
|
||||
if CLIENT then
|
||||
---Creates a client-side setting.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@param sDescription? string
|
||||
---@param sGroup? string
|
||||
---@param nMin? number
|
||||
---@param nMax? number
|
||||
---@return SF2Convar
|
||||
---@client
|
||||
function StormFox2.Setting.AddCL(sName,vDefaultVar,sDescription,sGroup, nMin, nMax)
|
||||
if settings[sName] then return settings[sName] end -- Already added
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t.sName = sName
|
||||
t.type = type(vDefaultVar)
|
||||
if CLIENT then
|
||||
if fileData[sName] ~= nil then
|
||||
t.value = StringToValue(fileData[sName], t.type)
|
||||
end
|
||||
if t.value == nil then
|
||||
local con = GetConVar("sf_" .. sName)
|
||||
if con then
|
||||
t.value = StringToValue(con:GetString(), t.type)
|
||||
end
|
||||
end
|
||||
if t.value == nil then -- If all fails, use the default
|
||||
t.value = vDefaultVar
|
||||
end
|
||||
end
|
||||
t.default = vDefaultVar
|
||||
t.server = false
|
||||
t.min = nMin
|
||||
t.max = nMax
|
||||
t:SetGroup( sGroup )
|
||||
t:SetDescription( sDescription )
|
||||
settings[sName] = t
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
---Tries to onvert to the given defaultvar to match the setting.
|
||||
---@param sName string
|
||||
---@param sString string
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.StringToType( sName, sString )
|
||||
local obj = settings[sName]
|
||||
if not obj then return sString end -- No idea
|
||||
return StringToValue( sString, obj.type )
|
||||
end
|
||||
|
||||
---Returns a setting and will try to convert to the given defaultvar type. Fallback to vDefaultVar if nil.
|
||||
---@param sName string
|
||||
---@param vDefaultVar? any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.Get(sName,vDefaultVar)
|
||||
local obj = settings[sName]
|
||||
if not obj then return vDefaultVar end
|
||||
return obj:GetValue()
|
||||
end
|
||||
|
||||
---Returns hte setting object.
|
||||
---@param sName string
|
||||
---@return SF2Convar
|
||||
---@shared
|
||||
function StormFox2.Setting.GetObject(sName)
|
||||
return settings[sName]
|
||||
end
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Sets a StormFox setting
|
||||
---------------------------------------------------------------------------]]
|
||||
local w_list = {
|
||||
"openweathermap_key", "openweathermap_real_lat", "openweathermap_real_lon"
|
||||
}
|
||||
--value_type["openweathermap_key"] = "string"
|
||||
--value_type["openweathermap_real_lat"] = "string"
|
||||
--value_type["openweathermap_real_lon"] = "string"
|
||||
|
||||
local function CallBack( sName, newVar, oldVar)
|
||||
if not settingCallback[sName] then return end
|
||||
for id, fFunc in pairs( settingCallback[sName] ) do
|
||||
if isstring(id) or IsValid(id) then
|
||||
fFunc(newVar, oldVar, sName, id)
|
||||
else -- Invalid
|
||||
settingCallback[sName][id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Tries to set a setting.
|
||||
---@param sName string
|
||||
---@param vVar any
|
||||
---@param bDontSave boolean
|
||||
---@return boolean saved
|
||||
function StormFox2.Setting.Set(sName,vVar, bDontSave)
|
||||
-- Check if valid
|
||||
local obj = settings[sName]
|
||||
if not obj then
|
||||
StormFox2.Warning("Invalid setting: " .. sName .. "!")
|
||||
return false
|
||||
end
|
||||
-- Check the variable
|
||||
if obj.type ~= type(vVar) then
|
||||
if type(vVar) == "string" then -- Try and convert it
|
||||
vVar = StringToValue( vVar, obj.type )
|
||||
else
|
||||
StormFox2.Warning("Invalid variable: " .. sName .. "!")
|
||||
return false
|
||||
end
|
||||
if vVar == nil then return false end -- Failed
|
||||
end
|
||||
-- Check min and max
|
||||
if obj.type == "number" and obj.min then
|
||||
vVar = math.max(vVar, obj.min)
|
||||
end
|
||||
if obj.type == "number" and obj.max then
|
||||
vVar = math.min(vVar, obj.max)
|
||||
end
|
||||
-- Check for duplicates
|
||||
local oldVar = obj:GetValue()
|
||||
if oldVar == vVar then return end -- Same value, ignore
|
||||
-- We need to ask the server to change this setting. This isn't ours
|
||||
if CLIENT and obj:IsServer() then
|
||||
if StormFox2.Permission then
|
||||
StormFox2.Permission.RequestSetting(sName, vVar)
|
||||
else
|
||||
StormFox2.Warning("Unable to ask server to change: " .. sName .. "!")
|
||||
end
|
||||
return false
|
||||
end
|
||||
-- Save the value
|
||||
-- Make callbacks
|
||||
local oB = blockSaveFile -- Editing a setting, might change others. Only save after we're done
|
||||
blockSaveFile = true
|
||||
local oldVar = obj.value
|
||||
obj.value = vVar
|
||||
-- Callback
|
||||
CallBack(sName, vVar, oldVar)
|
||||
blockSaveFile = oB -- We're done changing settings, save if we can
|
||||
if not blockSaveFile and not bDontSave then
|
||||
if not (sName == "mapfile" or sName == "mapfile_cl") then
|
||||
saveToFile()
|
||||
end
|
||||
end
|
||||
cache[sName] = nil -- Delete cache
|
||||
-- Tell all clients about it
|
||||
if SERVER then
|
||||
if not obj:IsSecret() then
|
||||
net.Start( StormFox2.Net.Settings )
|
||||
net.WriteUInt(NET_UPDATE, 3)
|
||||
net.WriteString(sName)
|
||||
net.WriteType(vVar)
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
--[[<Shared>------------------------------------------------------------------
|
||||
Gets called when a StormFox setting changes.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run("StormFox2.Setting.Change",sName,vVar, oldVar)
|
||||
return true
|
||||
end
|
||||
-- Server and Clientside NET
|
||||
if CLIENT then
|
||||
net.Receive(StormFox2.Net.Settings,function(len)
|
||||
local _type = net.ReadUInt(3)
|
||||
if _type == NET_UPDATE then
|
||||
local sName = net.ReadString()
|
||||
local var = net.ReadType()
|
||||
local obj = settings[sName]
|
||||
if not obj then
|
||||
StormFox2.Warning("Server tried to set an unknown setting: " .. sName)
|
||||
return
|
||||
end
|
||||
if not obj:IsServer() then
|
||||
StormFox2.Warning("Server tried to set a clientside setting: " .. sName)
|
||||
else
|
||||
local oldVar = obj.value
|
||||
obj.value = var
|
||||
cache[sName] = var
|
||||
-- Callback
|
||||
CallBack(sName, var, oldVar)
|
||||
hook.Run("StormFox2.Setting.Change",sName,obj.value,oldVar)
|
||||
end
|
||||
elseif _type == NET_ALLSETTINGS then
|
||||
local tab = net.ReadTable() -- I'm lazy
|
||||
for sName, vVar in pairs( tab ) do
|
||||
local obj = settings[sName]
|
||||
if not obj then -- So this setting "might" be used later. Cache the setting-value and ignore
|
||||
postSettingChace[sName] = vVar
|
||||
-- StormFox2.Warning("Server tried to set an unknown setting: " .. sName)
|
||||
continue
|
||||
end
|
||||
if not obj:IsServer() then -- This is a clientside setting. Nope.AVI
|
||||
StormFox2.Warning("Server tried to set a clientside setting: " .. sName)
|
||||
else
|
||||
local oldVar = obj.value
|
||||
obj.value = vVar
|
||||
cache[sName] = vVar
|
||||
-- Callback
|
||||
CallBack(sName, vVar, oldVar)
|
||||
hook.Run("StormFox2.Setting.Change",sName,obj.value,oldVar)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
hook.Add("StormFox2.data.initspawn", "StormFox2.setting.send", function( ply )
|
||||
net.Start( StormFox2.Net.Settings )
|
||||
net.WriteUInt(NET_ALLSETTINGS, 3)
|
||||
for sName, obj in pairs( settings ) do
|
||||
if not obj then continue end
|
||||
if obj:IsSecret() then continue end
|
||||
net.WriteType( sName )
|
||||
net.WriteType( obj:GetValue() )
|
||||
end
|
||||
-- End of table
|
||||
net.WriteType( nil )
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
---Calls the function when the given setting changes.
|
||||
---fFunc will be called with: vNewVariable, vOldVariable, ConVarName, sID.
|
||||
---Unlike convars, server-setings will also be triggered on the clients too.
|
||||
---@param sName string
|
||||
---@param fFunc function
|
||||
---@param sID any
|
||||
---@shared
|
||||
function StormFox2.Setting.Callback(sName,fFunc,sID)
|
||||
if not settingCallback[sName] then settingCallback[sName] = {} end
|
||||
settingCallback[sName][sID or "default"] = fFunc
|
||||
end
|
||||
|
||||
--hook.Add("StormFox2.Setting.Change", "StormFox2.Setting.Callbacks", function(sName, vVar, oldVar)
|
||||
--if not settingCallback[sName] then return end
|
||||
--for id, fFunc in pairs( settingCallback[sName] ) do
|
||||
-- fFunc(vVar, oldVar, sName, id)
|
||||
--end
|
||||
--end)
|
||||
|
||||
---Same as StormFox2.Setting.Get, however this will cache the result.
|
||||
---@param sName string
|
||||
---@param vDefaultVar any
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCache(sName,vDefaultVar)
|
||||
if cache[sName] then return cache[sName] end
|
||||
local var = StormFox2.Setting.Get(sName,vDefaultVar)
|
||||
cache[sName] = var
|
||||
return var
|
||||
end
|
||||
|
||||
---Returns the default setting
|
||||
---@param sName string
|
||||
---@return any
|
||||
---@shared
|
||||
function StormFox2.Setting.GetDefault(sName)
|
||||
local obj = settings[sName]
|
||||
if not obj then return nil end
|
||||
return obj:GetDefault()
|
||||
end
|
||||
|
||||
---Returns all known settings. Clients will reitrhn both server and client settings.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Setting.GetAll()
|
||||
return table.GetKeys( settings )
|
||||
end
|
||||
|
||||
---Returns all server settings.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Setting.GetAllServer()
|
||||
-- Server only has server settings
|
||||
if SERVER then return StormFox2.Setting.GetAll() end
|
||||
-- Make list
|
||||
local t = {}
|
||||
for sName,obj in pairs(settings) do
|
||||
if not obj:IsServer() then continue end
|
||||
table.insert(t, sName)
|
||||
end
|
||||
return t
|
||||
end
|
||||
if CLIENT then
|
||||
---Returns all client settings.
|
||||
---@return table
|
||||
function StormFox2.Setting.GetAllClient()
|
||||
local t = {}
|
||||
for sName,obj in pairs(settings) do
|
||||
if obj:IsServer() then continue end
|
||||
table.insert(t, sName)
|
||||
end
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the valuetype of the setting. This can allow special types like tables .. ect
|
||||
local type_override = {}
|
||||
|
||||
---Returns the settigns variable type.
|
||||
---@param sName string
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetType( sName )
|
||||
if type_override[sName] then return type_override[sName] end
|
||||
local obj = settings[sName]
|
||||
if not obj then return end
|
||||
return obj.type
|
||||
end
|
||||
--[[ Type:
|
||||
- number
|
||||
- string
|
||||
- float
|
||||
- boolean
|
||||
- A table of options { [value] = "description" }
|
||||
- special_float
|
||||
Marks below 0 as "off"
|
||||
- time
|
||||
- temp / temperature
|
||||
- Time_toggle
|
||||
]]
|
||||
|
||||
---Sets setting's variable type. Can also use special types like "time".
|
||||
---@param sName string
|
||||
---@param sType string
|
||||
---@param tSortOrter table
|
||||
---@shared
|
||||
function StormFox2.Setting.SetType( sName, sType, tSortOrter )
|
||||
if type(sType) == "nil" then -- Reset it
|
||||
StormFox2.Warning("Can't make the setting a nil-type!")
|
||||
end
|
||||
if type(sType) == "boolean" then
|
||||
type_override[sName] = "boolean"
|
||||
elseif type(sType) == "number" then
|
||||
type_override[sName] = "number"
|
||||
elseif type(sType) == "table" then -- A table is a list of options
|
||||
type_override[sName] = {sType, tSortOrter}
|
||||
else
|
||||
if sType == "bool" then
|
||||
type_override[sName] = "boolean"
|
||||
else
|
||||
type_override[sName] = string.lower(sType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets all stormfox settings to default.
|
||||
if SERVER then
|
||||
local obj = StormFox2.Setting.AddSV("mapfile", false)
|
||||
obj:AddCallback(StormFox2.Setting.UseMapFile)
|
||||
obj:SetValue( StormFox2.Setting.IsUsingMapFile() )
|
||||
---Returns all settings back to default.
|
||||
---@server
|
||||
function StormFox2.Setting.Reset()
|
||||
blockSaveFile = true
|
||||
for sName, obj in pairs(settings) do
|
||||
if sName == "mapfile" then continue end
|
||||
obj:Revert()
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were reset to default values. You should restart!")
|
||||
cache = {}
|
||||
end
|
||||
else
|
||||
StormFox2.Setting.AddSV("mapfile", false)
|
||||
local obj = StormFox2.Setting.AddCL("mapfile_cl", false)
|
||||
obj:AddCallback(StormFox2.Setting.UseMapFile)
|
||||
obj:SetValue( StormFox2.Setting.IsUsingMapFile() )
|
||||
---Returns all settings back to default.
|
||||
---@client
|
||||
function StormFox2.Setting.Reset()
|
||||
blockSaveFile = true
|
||||
for _, sName in ipairs(StormFox2.Setting.GetAllClient()) do
|
||||
if sName == "mapfile_cl" then continue end
|
||||
local obj = setting[sName]
|
||||
if not obj then continue end
|
||||
obj:Revert()
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were reset to default values. You should rejoin!")
|
||||
cache = {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets and sets StormFox server setting
|
||||
if SERVER then
|
||||
---Parses a CVS string and applies all settings to SF2.
|
||||
---@param str string
|
||||
---@server
|
||||
function StormFox2.Setting.SetCVS( str )
|
||||
local t = string.Explode(",", str)
|
||||
blockSaveFile = true
|
||||
for i = 1, #t, 2 do
|
||||
local sName, var = t[i], t[i+1] or nil
|
||||
if string.len(sName) < 1 or not var then continue end
|
||||
local obj = StormFox2.Setting.GetObject(sName )
|
||||
if not obj then
|
||||
StormFox2.Warning("Invalid setting: " .. sName .. ".")
|
||||
continue
|
||||
else
|
||||
obj:SetValue(var)
|
||||
end
|
||||
end
|
||||
blockSaveFile = false
|
||||
saveToFile()
|
||||
StormFox2.Warning("All settings were updated. You should restart!")
|
||||
end
|
||||
end
|
||||
|
||||
local exlist = {"openweathermap_real_lat", "openweathermap_real_lon", "openweathermap_key"}
|
||||
---Compiles all server-settigns into a CVS string.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCVS()
|
||||
local c = ""
|
||||
for sName, obj in pairs(settings) do
|
||||
if obj:IsSecret() then continue end
|
||||
if not obj:IsServer() then continue end
|
||||
c = c .. sName .. "," .. obj:GetString() .. ","
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
---Compiles all default server-settings into a CVS string.
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Setting.GetCVSDefault()
|
||||
local c = ""
|
||||
for sName, obj in pairs(settings) do
|
||||
if obj:IsSecret() then continue end -- Justi n case, so people don't share hidden settings
|
||||
if not obj:IsServer() then continue end
|
||||
c = c .. sName .. "," .. ValueToString(obj:GetDefault(), obj:GetType()) .. ","
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
-- Disable SF2
|
||||
StormFox2.Setting.AddSV("enable", true, nil, "Start")
|
||||
StormFox2.Setting.AddSV("allow_csenable", engine.ActiveGamemode() == "sandbox", nil, "Start")
|
||||
if CLIENT then
|
||||
StormFox2.Setting.AddCL("clenable", true, nil, "Start")
|
||||
end
|
||||
|
||||
---Returns true if SF2 is enabled.
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Setting.SFEnabled()
|
||||
if not StormFox2.Setting.GetCache("enable", true) then return false end
|
||||
if SERVER or not StormFox2.Setting.GetCache("allow_csenable", false) then return true end
|
||||
return StormFox2.Setting.GetCache("clenable", true)
|
||||
end
|
||||
371
lua/stormfox2/lib/sh_terrain.lua
Normal file
371
lua/stormfox2/lib/sh_terrain.lua
Normal file
@@ -0,0 +1,371 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
--[[
|
||||
Terrain control. Changes the ground
|
||||
|
||||
Terrain.Create( sName ) Creates a new terrain type and stores it
|
||||
Terrain.Get( sName ) Returns the terrain.
|
||||
Terrain.Set( sName ) Sets the terrain. (This should only be done serverside)
|
||||
Terrain.GetCurrent() Returns the terrain obj or nil.
|
||||
StormFox2.Terrain.Reset() Resets the terrain to default.
|
||||
StormFox2.Terrain.HasMaterialChanged( iMaterial ) Returns true if the terrain has changed the material.
|
||||
|
||||
Terrain Meta:
|
||||
:LockUntil( fFunc ) Makes the terrian
|
||||
:MakeFootprints( sndList, sndName ) Makes footprints. Allows to overwrite footstep sounds.
|
||||
:AddTextureSwap( material, basetexture, basetextire2 ) Changes a materials textures.
|
||||
:RenderWindow( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindowRefract( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindow64x64( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:RenderWindowRefract64x64( width, height ) A function that renders a window-texure. (Weather will trump this)
|
||||
:Apply Applies the terrain (This won't reset old terrain)
|
||||
|
||||
Hooks:
|
||||
StormFox2.terrain.footstep Entity foot[0 = left,1 = right] sTexture bTerrainTexture
|
||||
]]
|
||||
|
||||
---@class SF2Terrain
|
||||
local meta = {}
|
||||
meta.__index = meta
|
||||
meta.__tostring = function(self) return "SF_TerrainType[" .. (self.Name or "Unknwon") .. "]" end
|
||||
meta.__eq = function(self, other)
|
||||
if type(other) ~= "table" then return false end
|
||||
if not other.Name then return false end
|
||||
return other.Name == self.Name
|
||||
end
|
||||
debug.getregistry()["SFTerrain"] = meta
|
||||
local terrains = {}
|
||||
StormFox2.Terrain = {}
|
||||
|
||||
--- Creates a new terrain type, stores and returns it.
|
||||
---@param sName string
|
||||
---@return SF2Terrain
|
||||
---@shared
|
||||
function StormFox2.Terrain.Create( sName )
|
||||
local t = {}
|
||||
t.Name = sName
|
||||
setmetatable(t, meta)
|
||||
terrains[sName] = t
|
||||
t.swap = {}
|
||||
return t
|
||||
end
|
||||
|
||||
local CURRENT_TERRAIN
|
||||
---Returns the current applies terrain.
|
||||
---@return SF2Terrain|nil
|
||||
---@shared
|
||||
function StormFox2.Terrain.GetCurrent()
|
||||
return CURRENT_TERRAIN
|
||||
end
|
||||
|
||||
---Returns a terrain by name.
|
||||
---@param sName string
|
||||
---@return SF2Terrain|nil
|
||||
---@shared
|
||||
function StormFox2.Terrain.Get( sName )
|
||||
if not sName then return end
|
||||
return terrains[sName]
|
||||
end
|
||||
|
||||
-- Makes the terrain stay until this function returns true or another terrain overwrites.
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:LockUntil( fFunc )
|
||||
self.lock = fFunc
|
||||
end
|
||||
|
||||
---Sets the ground texture. e.i; snow texture.
|
||||
---@param iTexture string
|
||||
---@param bOnlyGround boolean
|
||||
---@shared
|
||||
function meta:SetGroundTexture( iTexture, bOnlyGround )
|
||||
self.ground = iTexture
|
||||
self.only_ground = bOnlyGround
|
||||
end
|
||||
|
||||
-- Adds a texture swap for when this terrain gets applied.
|
||||
---@param mMaterial Material|string
|
||||
---@param basetexture string
|
||||
---@param basetextire2 string
|
||||
---@shared
|
||||
function meta:AddTextureSwap( mMaterial, basetexture, basetextire2 )
|
||||
if type(mMaterial) ~= "IMaterial" then
|
||||
mMaterial = Material(mMaterial)
|
||||
end
|
||||
if not basetexture and not basetextire2 then return end
|
||||
self.swap[mMaterial] = { basetexture, basetextire2 }
|
||||
end
|
||||
|
||||
---Makes footprints and allows to overwrite default footstep sounds.
|
||||
---@param bool boolean
|
||||
---@param sndList? table
|
||||
---@param sndName? string
|
||||
---@param OnPrint? function
|
||||
---@shared
|
||||
function meta:MakeFootprints( bool, sndList, sndName, OnPrint )
|
||||
self.footprints = bool
|
||||
if sndList or sndName then
|
||||
self.footprintSnds = {sndList, sndName}
|
||||
end
|
||||
self.footstepFunc = OnPrint
|
||||
self.footstepLisen = bool or sndList or sndName or OnPrint
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindow( fFunc )
|
||||
self.windRender = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindowRefract( fFunc )
|
||||
self.windRenderRef = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindow64x64( fFunc )
|
||||
self.windRender64 = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure. (Weather will trump this)
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function meta:RenderWindowRefract64x64( fFunc )
|
||||
self.windRenderRef64 = fFunc
|
||||
end
|
||||
|
||||
-- Texture handler
|
||||
_STORMFOX_TEXCHANGES = _STORMFOX_TEXCHANGES or {} -- List of changed materials.
|
||||
_STORMFOX_TEXORIGINAL = _STORMFOX_TEXORIGINAL or {} -- This is global, just in case.
|
||||
local footStepLisen = false -- If set to true, will enable footprints.
|
||||
|
||||
local function StringTex(iTex)
|
||||
if not iTex then return end
|
||||
if type(iTex) == "string" then return iTex end
|
||||
return iTex:GetName()
|
||||
end
|
||||
|
||||
local function HasChanged( self, materialTexture )
|
||||
local mat = self:GetName() or "unknown"
|
||||
local b = materialTexture == "$basetexture2" and 2 or 1
|
||||
return _STORMFOX_TEXCHANGES[mat] and _STORMFOX_TEXCHANGES[mat][b] or false
|
||||
end
|
||||
|
||||
---Returns true if the material has changed.
|
||||
---@param iMaterial Material
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.HasMaterialChanged( iMaterial )
|
||||
local mat = iMaterial:GetName() or iMaterial
|
||||
return _STORMFOX_TEXCHANGES[mat] and true or false
|
||||
end
|
||||
|
||||
---Returns the original texture for said material.
|
||||
---@param iMaterial Material
|
||||
---@return stirng
|
||||
---@shared
|
||||
function StormFox2.Terrain.GetOriginalTexture( iMaterial )
|
||||
local mat = iMaterial:GetName() or iMaterial
|
||||
return _STORMFOX_TEXORIGINAL[mat] and _STORMFOX_TEXORIGINAL[mat][1]
|
||||
end
|
||||
|
||||
-- We're going to overwrite SetTexture. As some mods might change the default texture.
|
||||
local mat_meta = FindMetaTable("IMaterial")
|
||||
STORMFOX_TEX_APPLY = STORMFOX_TEX_APPLY or mat_meta.SetTexture
|
||||
function mat_meta:SetTexture(materialTexture, texture)
|
||||
-- Check if it is basetexutre or basetexture2 we're changing.
|
||||
if materialTexture ~= "$basetexture" and materialTexture ~= "$basetexture2" then
|
||||
return STORMFOX_TEX_APPLY( self, materialTexture, texture )
|
||||
end
|
||||
-- Overwrite the original texture list.
|
||||
local mat = self:GetName() or "unknown"
|
||||
if not _STORMFOX_TEXORIGINAL[mat] then _STORMFOX_TEXORIGINAL[mat] = {} end
|
||||
if materialTexture == "$basetexture" then
|
||||
_STORMFOX_TEXORIGINAL[mat][1] = StringTex(texture)
|
||||
else
|
||||
_STORMFOX_TEXORIGINAL[mat][2] = StringTex(texture)
|
||||
end
|
||||
-- If we havn't changed the texture, allow change.
|
||||
if not HasChanged(self, materialTexture) then
|
||||
return STORMFOX_TEX_APPLY( self, materialTexture, texture )
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets the material. Returns false if unable to reset.
|
||||
local function ResetMaterial( self )
|
||||
local mat = self:GetName() or "unknown"
|
||||
if not _STORMFOX_TEXCHANGES[mat] or not _STORMFOX_TEXORIGINAL[mat] then return false end
|
||||
if _STORMFOX_TEXCHANGES[mat][1] and _STORMFOX_TEXORIGINAL[mat][1] then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture", _STORMFOX_TEXORIGINAL[mat][1] )
|
||||
end
|
||||
if _STORMFOX_TEXCHANGES[mat][2] and _STORMFOX_TEXORIGINAL[mat][2] then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture2", _STORMFOX_TEXORIGINAL[mat][2] )
|
||||
end
|
||||
_STORMFOX_TEXCHANGES[mat] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
-- Set the material
|
||||
local function SetMat(self, tex1, tex2)
|
||||
if not tex1 and not tex2 then return end
|
||||
local mat = self:GetName() or "unknown"
|
||||
-- Save the default texture
|
||||
if not _STORMFOX_TEXORIGINAL[mat] then _STORMFOX_TEXORIGINAL[mat] = {} end
|
||||
if tex1 and not _STORMFOX_TEXORIGINAL[mat][1] then
|
||||
_STORMFOX_TEXORIGINAL[mat][1] = StringTex(self:GetTexture("$basetexture"))
|
||||
end
|
||||
if tex2 and not _STORMFOX_TEXORIGINAL[mat][2] then
|
||||
_STORMFOX_TEXORIGINAL[mat][2] = StringTex(self:GetTexture("$basetexture2"))
|
||||
end
|
||||
-- Set texture
|
||||
if tex1 then
|
||||
if CLIENT then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture", tex1 )
|
||||
end
|
||||
if not _STORMFOX_TEXCHANGES[ mat ] then _STORMFOX_TEXCHANGES[ mat ] = {} end
|
||||
_STORMFOX_TEXCHANGES[ mat ][ 1 ] = true
|
||||
end
|
||||
if tex2 then
|
||||
if CLIENT then
|
||||
STORMFOX_TEX_APPLY( self, "$basetexture2", tex2 )
|
||||
end
|
||||
if not _STORMFOX_TEXCHANGES[ mat ] then _STORMFOX_TEXCHANGES[ mat ] = {} end
|
||||
_STORMFOX_TEXCHANGES[ mat ][ 2 ] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Resets the terrain to default. Setting bNoUpdate to true on the server, will not notify the clients or relays.
|
||||
---@param bNoUpdate boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.Reset( bNoUpdate )
|
||||
--print("Reset")
|
||||
if SERVER and not bNoUpdate then
|
||||
StormFox2.Map.CallLogicRelay("terrain_clear")
|
||||
end
|
||||
CURRENT_TERRAIN = nil
|
||||
if SERVER and not bNoUpdate then
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( "" )
|
||||
net.Broadcast()
|
||||
end
|
||||
if next(_STORMFOX_TEXCHANGES) == nil then return end
|
||||
for tex,_ in pairs( _STORMFOX_TEXCHANGES ) do
|
||||
local mat = Material( tex )
|
||||
if not ResetMaterial( mat ) then
|
||||
StormFox2.Warning( "Can't reset [" .. tostring( mat ) .. "]." )
|
||||
end
|
||||
end
|
||||
_STORMFOX_TEXCHANGES = {}
|
||||
end
|
||||
|
||||
--- Sets the terrain. (This should only be done serverside)
|
||||
---@param sName string
|
||||
---@return boolean
|
||||
---@shared
|
||||
function StormFox2.Terrain.Set( sName )
|
||||
-- Apply terrain.
|
||||
local t = StormFox2.Terrain.Get( sName )
|
||||
if not t then
|
||||
StormFox2.Terrain.Reset()
|
||||
return false
|
||||
end
|
||||
StormFox2.Terrain.Reset( true )
|
||||
t:Apply()
|
||||
if SERVER then
|
||||
StormFox2.Map.CallLogicRelay( "terrain_" .. string.lower(sName) )
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Reapplies the current terrain
|
||||
---@shared
|
||||
function StormFox2.Terrain.Update()
|
||||
local terrain = StormFox2.Terrain.GetCurrent()
|
||||
if not terrain then return end
|
||||
StormFox2.Terrain.Reset( true )
|
||||
terrain:Apply()
|
||||
end
|
||||
|
||||
local NO_TYPE = -1
|
||||
local DIRTGRASS_TYPE = 0
|
||||
local ROOF_TYPE = 1
|
||||
local ROAD_TYPE = 2
|
||||
local PAVEMENT_TYPE = 3
|
||||
|
||||
local function checkType(n, bOnlkyG)
|
||||
if not n then return false end
|
||||
if n == 0 then return true end
|
||||
if n == 1 and not bOnlkyG then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Applies the terrain (This won't reset old terrain)
|
||||
function meta:Apply()
|
||||
CURRENT_TERRAIN = self
|
||||
if SERVER then
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( CURRENT_TERRAIN.Name )
|
||||
net.Broadcast()
|
||||
end
|
||||
-- Swap materials
|
||||
if self.swap then
|
||||
for mat,tab in pairs( self.swap ) do
|
||||
SetMat( mat, tab[1], tab[2] )
|
||||
end
|
||||
end
|
||||
-- Set ground
|
||||
if self.ground then
|
||||
for materialName,tab in pairs( StormFox2.Map.GetTextureTree() ) do
|
||||
local mat = Material( materialName )
|
||||
local a,b = checkType(tab[1], self.only_ground), checkType(tab[2], self.only_ground)
|
||||
SetMat( mat, a and self.ground,b and self.ground)
|
||||
end
|
||||
end
|
||||
footStepLisen = self.footprints or self.footprintSnds
|
||||
end
|
||||
|
||||
-- NET
|
||||
if SERVER then
|
||||
net.Receive(StormFox2.Net.Terrain, function(len, ply) -- OI, what terrain?
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteString( CURRENT_TERRAIN and CURRENT_TERRAIN.Name or "" )
|
||||
net.Send(ply)
|
||||
end)
|
||||
else
|
||||
net.Receive(StormFox2.Net.Terrain, function(len)
|
||||
local sName = net.ReadString()
|
||||
local b = StormFox2.Terrain.Set( sName )
|
||||
--"Terrain Recived: ", sName, b)
|
||||
|
||||
end)
|
||||
-- Ask the server
|
||||
hook.Add("StormFox2.InitPostEntity", "StormFox2.terrain.init", function()
|
||||
timer.Simple(1, function()
|
||||
net.Start(StormFox2.Net.Terrain)
|
||||
net.WriteBit(1)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
-- A bit ballsy, but lets try.
|
||||
hook.Add("ShutDown","StormFox2.Terrain.Clear",function()
|
||||
StormFox2.Msg("Reloading all changed materials ..")
|
||||
for k, _ in pairs( _STORMFOX_TEXORIGINAL ) do
|
||||
RunConsoleCommand("mat_reloadmaterial", k)
|
||||
end
|
||||
end)
|
||||
256
lua/stormfox2/lib/sh_util.lua
Normal file
256
lua/stormfox2/lib/sh_util.lua
Normal file
@@ -0,0 +1,256 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Useful functions
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
StormFox2.util = {}
|
||||
local cache = {}
|
||||
|
||||
---Returns the OBBMins and OBBMaxs of a model.
|
||||
---@param sModel string
|
||||
---@return Vector MinSize
|
||||
---@return Vector MaxSize
|
||||
---@shared
|
||||
function StormFox2.util.GetModelSize(sModel)
|
||||
if cache[sModel] then return cache[sModel][1],cache[sModel][2] end
|
||||
if not file.Exists(sModel,"GAME") then
|
||||
cache[sModel] = {Vector(0,0,0),Vector(0,0,0)}
|
||||
return cache[sModel]
|
||||
end
|
||||
local f = file.Open(sModel,"r", "GAME")
|
||||
f:Seek(104)
|
||||
local hullMin = Vector( f:ReadFloat(),f:ReadFloat(),f:ReadFloat())
|
||||
local hullMax = Vector( f:ReadFloat(),f:ReadFloat(),f:ReadFloat())
|
||||
f:Close()
|
||||
cache[sModel] = {hullMin,hullMax}
|
||||
return hullMin,hullMax
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
--[[-----------------------------------------------------------------
|
||||
Calcview results
|
||||
---------------------------------------------------------------------------]]
|
||||
local view = {}
|
||||
view.pos = Vector(0,0,0)
|
||||
view.ang = Angle(0,0,0)
|
||||
view.fov = 0
|
||||
view.drawviewer = false
|
||||
local otherPos, otherAng, otherFOV
|
||||
local a = true
|
||||
hook.Add("RenderScene", "StormFox2.util.EyeHack", function(pos, ang,fov)
|
||||
if not a then return end
|
||||
otherPos, otherAng, otherFOV = pos, ang,fov
|
||||
a = false
|
||||
end)
|
||||
|
||||
hook.Add("PostRender", "StormFox2.util.EyeHack", function()
|
||||
local tab = render.GetViewSetup and render.GetViewSetup() or {}
|
||||
view.pos = tab.origin or otherPos or EyePos()
|
||||
view.ang = tab.angles or otherAng or EyeAngles()
|
||||
view.fov = tab.fov or otherFOV or 90
|
||||
view.drawviewer = LocalPlayer():ShouldDrawLocalPlayer()
|
||||
a = true
|
||||
end)
|
||||
|
||||
---Returns the last calcview result.
|
||||
---@return table
|
||||
---@client
|
||||
function StormFox2.util.GetCalcView()
|
||||
return view
|
||||
end
|
||||
|
||||
---Returns the last camera position.
|
||||
---@return Vector
|
||||
---@client
|
||||
function StormFox2.util.RenderPos()
|
||||
return view.pos or EyePos()
|
||||
end
|
||||
|
||||
---Returns the last camera angle.
|
||||
---@return Angle
|
||||
---@client
|
||||
function StormFox2.util.RenderAngles()
|
||||
return view.ang or RenderAngles()
|
||||
end
|
||||
|
||||
--[[<Client>-----------------------------------------------------------------
|
||||
Returns the current viewentity
|
||||
---------------------------------------------------------------------------]]
|
||||
local viewEntity
|
||||
hook.Add("Think", "StormFox2.util.ViewEnt", function()
|
||||
local lp = LocalPlayer()
|
||||
if not IsValid(lp) then return end
|
||||
local p = lp:GetViewEntity() or lp
|
||||
if p.InVehicle and p:InVehicle() and p == lp then
|
||||
viewEntity = p:GetVehicle() or p
|
||||
else
|
||||
viewEntity = p
|
||||
end
|
||||
end)
|
||||
---Returns the current viewentity.
|
||||
---@return Entity
|
||||
---@client
|
||||
function StormFox2.util.ViewEntity()
|
||||
return IsValid(viewEntity) and viewEntity or LocalPlayer()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Color interpolation suck.
|
||||
Mixing an orange and blue color can result in a greenish one.
|
||||
This is not how sky colors work, so we make our own CCT object here that can be mixed instead.
|
||||
]]
|
||||
|
||||
local log,Clamp,pow = math.log, math.Clamp, math.pow
|
||||
---@class SF2CCT_Color
|
||||
local meta = {}
|
||||
function meta.__index( a, b )
|
||||
return meta[b] or a._col[b]
|
||||
end
|
||||
meta.__MetaName = "CCT_Color"
|
||||
local function CCTToRGB( nKelvin )
|
||||
kelvin = math.Clamp(nKelvin, 1000, 40000)
|
||||
local tmp = kelvin / 100
|
||||
local r, g, b = 0,0,0
|
||||
if tmp <= 66 then
|
||||
r = 255
|
||||
g = 99.4708025861 * log(tmp) - 161.1195681661
|
||||
else
|
||||
r = 329.698727446 * pow(tmp - 60, -0.1332047592)
|
||||
g = 288.1221695283 * pow(tmp - 60, -0.0755148492)
|
||||
end
|
||||
if tmp >= 66 then
|
||||
b = 255
|
||||
elseif tmp <= 19 then
|
||||
b = 0
|
||||
else
|
||||
b = 138.5177312231 * log(tmp - 10) - 305.0447927307
|
||||
end
|
||||
if nKelvin < 1000 then
|
||||
local f = (nKelvin / 1000)
|
||||
r = r * f
|
||||
g = g * f
|
||||
b = b * f
|
||||
end
|
||||
return Color(Clamp(r, 0, 255), Clamp(g, 0, 255), Clamp(b, 0, 255))
|
||||
end
|
||||
|
||||
---Returns a CCT Color object.
|
||||
---@param kelvin number
|
||||
---@return SF2CCT_Color
|
||||
function StormFox2.util.CCTColor( kelvin )
|
||||
local t = {}
|
||||
setmetatable(t, meta)
|
||||
t._kelvin = kelvin
|
||||
t._col = CCTToRGB( kelvin )
|
||||
return t
|
||||
end
|
||||
|
||||
function meta:ToRGB()
|
||||
return self._col
|
||||
end
|
||||
|
||||
function meta:SetKelvin( kelvin )
|
||||
self._kelvin = kelvin
|
||||
self._col = CCTToRGB( kelvin )
|
||||
return self
|
||||
end
|
||||
|
||||
function meta:GetKelvin()
|
||||
return self._kelvin
|
||||
end
|
||||
|
||||
function meta.__add( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = b:GetKelvin() + a
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() + b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t + b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta.__sub( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a - b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() - b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t - b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta.__mul( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a * b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() * b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t * b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
function meta:__div( a, b )
|
||||
local t = 0
|
||||
if type( a ) == "number" then
|
||||
t = a / b:GetKelvin()
|
||||
elseif type( b ) == "number" then
|
||||
t = a:GetKelvin() / b
|
||||
else
|
||||
if a.GetKelvin then
|
||||
t = a:GetKelvin()
|
||||
end
|
||||
if b.GetKelvin then
|
||||
t = t / b:GetKelvin()
|
||||
end
|
||||
end
|
||||
return StormFox2.util.CCTColor( t )
|
||||
end
|
||||
|
||||
---Renders a range of colors in the console.
|
||||
---@param from number
|
||||
---@param to number
|
||||
---@param len number
|
||||
function StormFox2.util.CCTColorDebug( from, to, len )
|
||||
len = len or 60
|
||||
from = from or 2200
|
||||
to = to or 12000
|
||||
local a = (to - from) / len
|
||||
Msg(from .. " [")
|
||||
for i = 1, len do
|
||||
MsgC(StormFox2.util.CCTColor(from + a * i) , "▉" )
|
||||
end
|
||||
Msg("] " .. to)
|
||||
MsgN()
|
||||
end
|
||||
44
lua/stormfox2/lib/sh_version.lua
Normal file
44
lua/stormfox2/lib/sh_version.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Checks the newest version
|
||||
if SERVER then
|
||||
-- Checks the workshop page for version number.
|
||||
local function RunCheck()
|
||||
http.Fetch(StormFox2.WorkShopURL, function(code)
|
||||
local lV = tonumber(string.match(code, "Version:(.-)<"))
|
||||
if not lV then return end -- Unable to locate last version
|
||||
if StormFox2.Version >= lV then return end -- Up to date
|
||||
StormFox2.Msg("Version " .. lV .. " is out!")
|
||||
StormFox2.Network.Set("stormfox_newv", lV)
|
||||
cookie.Set("sf_nextv", lV)
|
||||
end)
|
||||
end
|
||||
local function RunLogic()
|
||||
-- Check if a newer version is out
|
||||
local lV = cookie.GetNumber("sf_nextv", StormFox2.Version)
|
||||
if cookie.GetNumber("sf_nextvcheck", 0) > os.time() then
|
||||
if lV > StormFox2.Version then
|
||||
StormFox2.Msg("Version " .. lV .. " is out!")
|
||||
StormFox2.Network.Set("stormfox_newv", lV)
|
||||
end
|
||||
else
|
||||
RunCheck()
|
||||
cookie.Set("sf_nextvcheck", os.time() + 129600) -- Check in 1½ day
|
||||
end
|
||||
end
|
||||
hook.Add("stormfox2.preinit", "stormfox2.checkversion", RunLogic)
|
||||
end
|
||||
|
||||
-- Will return a version-number, if a new version is detected
|
||||
---@return number
|
||||
function StormFox2.NewVersion()
|
||||
return StormFox2.Data.Get("stormfox_newv")
|
||||
end
|
||||
287
lua/stormfox2/lib/sh_weather_meta.lua
Normal file
287
lua/stormfox2/lib/sh_weather_meta.lua
Normal file
@@ -0,0 +1,287 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
StormFox2.Weather = {}
|
||||
local Weathers = {}
|
||||
-- Diffrent stamps on where the sun are. (Remember, SF2 goes after sunrise/set)
|
||||
---@class SF_SKY_STAMP : number
|
||||
SF_SKY_DAY = 0
|
||||
SF_SKY_SUNRISE = 1
|
||||
SF_SKY_SUNSET = 2
|
||||
SF_SKY_CEVIL = 3
|
||||
SF_SKY_BLUE_HOUR = 4
|
||||
SF_SKY_NAUTICAL = 5
|
||||
SF_SKY_ASTRONOMICAL = 6
|
||||
SF_SKY_NIGHT = 7
|
||||
|
||||
---@class SF2_WeatherType
|
||||
local w_meta = {}
|
||||
w_meta.__index = w_meta
|
||||
w_meta.__tostring = function(self) return "SF_WeatherType[" .. (self.Name or "Unknwon") .. "]" end
|
||||
w_meta.MetaName = "SF-Weather"
|
||||
debug.getregistry()["SFWeather"] = w_meta
|
||||
|
||||
-- function for the generator. Returns true to allow. Function will be called with (day_temperature, time_start, time_duration, percent)
|
||||
---@deprecated
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:SetRequire(fFunc)
|
||||
self.Require = fFunc
|
||||
end
|
||||
|
||||
---Runs said function when the weather is applied.
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:SetInit(fFunc)
|
||||
self.Init = fFunc
|
||||
end
|
||||
|
||||
---Runs said function if the weather-amount change.
|
||||
---@param fFunc function
|
||||
---@deprecated
|
||||
---@shared
|
||||
function w_meta:SetOnChange(fFunc)
|
||||
self.OnChange = fFunc
|
||||
end
|
||||
|
||||
---Will always return true
|
||||
---@return boolean
|
||||
---@shared
|
||||
function w_meta:IsValid()
|
||||
return true
|
||||
end
|
||||
|
||||
---Creates and returns a new weather-type. Will not duplicate weather-types and instead return the one created before.
|
||||
---@param sName string
|
||||
---@param sInherit? string
|
||||
---@return SF2_WeatherType
|
||||
---@shared
|
||||
function StormFox2.Weather.Add( sName, sInherit )
|
||||
if Weathers[sName] then return Weathers[sName] end
|
||||
local t = {}
|
||||
t.ID = table.Count(Weathers) + 1
|
||||
t.Name = sName
|
||||
setmetatable(t, w_meta)
|
||||
if sName ~= "Clear" then -- Clear shouldn't inherit itself
|
||||
t.Inherit = sInherit or "Clear"
|
||||
end
|
||||
Weathers[sName] = t
|
||||
t.Function = {}
|
||||
t.Static = {}
|
||||
t.Dynamic = {}
|
||||
t.SunStamp = {}
|
||||
return t
|
||||
end
|
||||
|
||||
---Returns a weather-type by name.
|
||||
---@param sName string
|
||||
---@return SF2_WeatherType
|
||||
---@shared
|
||||
function StormFox2.Weather.Get( sName )
|
||||
return Weathers[sName]
|
||||
end
|
||||
|
||||
---Returns a list of all weather-types name.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetAll()
|
||||
return table.GetKeys( Weathers )
|
||||
end
|
||||
|
||||
---Returns all weathers that can be spawned.
|
||||
---@deprecated
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetAllSpawnable()
|
||||
local t = {}
|
||||
for w, v in pairs( Weathers ) do
|
||||
if v.spawnable and w ~= "Clear" then -- clear is default
|
||||
table.insert(t, w)
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local keys = {}
|
||||
local l_e,l_c, c_c = -1,0
|
||||
|
||||
---Sets a key-value.
|
||||
---@param sKey string
|
||||
---@param zVariable any|function
|
||||
---@param bStatic boolean
|
||||
---@shared
|
||||
function w_meta:Set(sKey,zVariable, bStatic)
|
||||
keys[sKey] = true
|
||||
l_c = CurTime()
|
||||
if type(zVariable) == "function" then
|
||||
self.Function[sKey] = zVariable
|
||||
elseif bStatic then
|
||||
self.Static[sKey] = zVariable
|
||||
else
|
||||
self.Dynamic[sKey] = zVariable
|
||||
end
|
||||
end
|
||||
|
||||
local r_list = {"Terrain", "windRender", "windRenderRef", "windRender64", "windRenderRef64"}
|
||||
---Returns all keys a weather has.
|
||||
---@return table
|
||||
---@shared
|
||||
function StormFox2.Weather.GetKeys()
|
||||
if l_c == l_e then
|
||||
return c_c
|
||||
end
|
||||
for k,v in ipairs(r_list) do
|
||||
keys[v] = nil
|
||||
end
|
||||
l_e = l_c
|
||||
c_c = table.GetKeys(keys)
|
||||
return c_c
|
||||
end
|
||||
|
||||
--- This function inserts a variable into a table. Using the STAMP as key.
|
||||
---@param sKey string
|
||||
---@param zVariable any
|
||||
---@param stamp SF_SKY_STAMP
|
||||
---@shared
|
||||
function w_meta:SetSunStamp(sKey, zVariable, stamp)
|
||||
keys[sKey] = true
|
||||
l_c = CurTime()
|
||||
if not self.SunStamp[sKey] then self.SunStamp[sKey] = {} end
|
||||
self.SunStamp[sKey][stamp] = zVariable
|
||||
end
|
||||
|
||||
--- Returns a copy of all variables with the given sunstamp, to another given sunstamp.
|
||||
---@param from_STAMP SF_SKY_STAMP
|
||||
---@param to_STAMP SF_SKY_STAMP
|
||||
---@shared
|
||||
function w_meta:CopySunStamp( from_STAMP, to_STAMP )
|
||||
for sKey,v in pairs(self.SunStamp) do
|
||||
if type(v) ~= "table" then continue end
|
||||
if not v[from_STAMP] then continue end
|
||||
self.SunStamp[sKey][to_STAMP] = v[from_STAMP] or nil
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local in_list = {}
|
||||
|
||||
---Returns a variable. If the variable is a function, it will be called with the current stamp.
|
||||
---Second argument will tell SF it is static and shouldn't be mixed
|
||||
---@param sKey string
|
||||
---@param SUNSTAMP SF_SKY_STAMP
|
||||
---@return any
|
||||
---@return boolean isStatic
|
||||
---@shared
|
||||
function w_meta:Get(sKey, SUNSTAMP )
|
||||
-- Fallback to day-stamp, if Last Steamp is nil-
|
||||
if not SUNSTAMP then
|
||||
SUNSTAMP = StormFox2.Sky.GetLastStamp() or SF_SKY_DAY
|
||||
end
|
||||
if self.Function[sKey] then
|
||||
return self.Function[sKey]( SUNSTAMP )
|
||||
elseif self.SunStamp[sKey] then
|
||||
if self.SunStamp[sKey][SUNSTAMP] ~= nil then
|
||||
return self.SunStamp[sKey][SUNSTAMP]
|
||||
end
|
||||
-- This sunstamp isn't set, try and elevate stamp and check
|
||||
if SUNSTAMP >= SF_SKY_CEVIL then
|
||||
for i = SF_SKY_CEVIL + 1, SF_SKY_NIGHT do
|
||||
if self.SunStamp[sKey][i] then
|
||||
return self.SunStamp[sKey][i]
|
||||
end
|
||||
end
|
||||
else
|
||||
for i = SF_SKY_CEVIL - 1, SF_SKY_DAY, -1 do
|
||||
if self.SunStamp[sKey][i] then
|
||||
return self.SunStamp[sKey][i]
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif self.Static[sKey] then
|
||||
return self.Static[sKey], true
|
||||
elseif self.Dynamic[sKey] then
|
||||
return self.Dynamic[sKey]
|
||||
end
|
||||
if self.Name == "Clear" then return end
|
||||
-- Check if we inherit
|
||||
if not self.Inherit then return nil end
|
||||
if not Weathers[self.Inherit] then return nil end -- Inherit is invalid
|
||||
if in_list[self.Inherit] == true then -- Loop detected
|
||||
StormFox2.Warning("WeatherData loop detected! multiple occurences of : " .. self.Inherit)
|
||||
return
|
||||
end
|
||||
in_list[self.Name] = true
|
||||
local a,b,c,d,e = Weathers[self.Inherit]:Get(sKey, SUNSTAMP)
|
||||
in_list = {}
|
||||
return a,b,c,d,e
|
||||
end
|
||||
end
|
||||
|
||||
---Sets the terrain for the weather. This can also be a function that returns a terrain object.
|
||||
---@param zTerrain SF2Terrain|function
|
||||
---@shared
|
||||
function w_meta:SetTerrain( zTerrain )
|
||||
self:Set( "Terrain", zTerrain )
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindow( fFunc )
|
||||
self._RenderWindow = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindowRefract( fFunc )
|
||||
self._RenderWindowRefract = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindow64x64( fFunc )
|
||||
self._RenderWindow64x64 = fFunc
|
||||
end
|
||||
|
||||
---A function that renders a window-texure
|
||||
---@param fFunc function
|
||||
---@shared
|
||||
function w_meta:RenderWindowRefract64x64( fFunc )
|
||||
self._RenderWindowRefract64x64 = fFunc
|
||||
end
|
||||
|
||||
---Returns the "lightlevel" of the skybox in a range of 0-255.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Weather.GetLuminance()
|
||||
local Col = StormFox2.Mixer.Get("bottomColor") or Color(255,255,255)
|
||||
return 0.2126 * Col.r + 0.7152 * Col.g + 0.0722 * Col.b
|
||||
end
|
||||
|
||||
-- Load the weathers once lib is done.
|
||||
hook.Add("stormfox2.postlib", "stormfox2.loadweathers", function()
|
||||
StormFox2.Setting.AddSV("weather_damage",true,nil, "Weather")
|
||||
|
||||
|
||||
StormFox2.Setting.AddSV("moonsize",20,nil,"Effects", 5, 500)
|
||||
|
||||
hook.Run("stormfox2.preloadweather", w_meta)
|
||||
for _,fil in ipairs(file.Find("stormfox2/weathers/*.lua","LUA")) do
|
||||
local succ = pcall(include,"stormfox2/weathers/" .. fil)
|
||||
if SERVER and succ then
|
||||
AddCSLuaFile("stormfox2/weathers/" .. fil)
|
||||
end
|
||||
end
|
||||
StormFox2.Weather.Loaded = true
|
||||
hook.Run("stormfox2.postloadweather")
|
||||
end)
|
||||
570
lua/stormfox2/lib/sh_wind.lua
Normal file
570
lua/stormfox2/lib/sh_wind.lua
Normal file
@@ -0,0 +1,570 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
StormFox2.Wind = StormFox2.Wind or {}
|
||||
local min,max,sqrt,abs = math.min,math.max,math.sqrt,math.abs
|
||||
local function ET(pos,pos2,mask,filter)
|
||||
local t = util.TraceLine( {
|
||||
start = pos,
|
||||
endpos = pos + pos2,
|
||||
mask = mask,
|
||||
filter = filter
|
||||
} )
|
||||
t.HitPos = t.HitPos or (pos + pos2)
|
||||
return t,t.HitSky
|
||||
end
|
||||
|
||||
-- Settings
|
||||
hook.Add("stormfox2.postlib", "stormfox2.windSettings",function()
|
||||
StormFox2.Setting.AddSV("windmove_players",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_foliate",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props",false,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_break",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_unweld",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_unfreeze",true,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_max",100,nil,"Weather")
|
||||
StormFox2.Setting.AddSV("windmove_props_makedebris",true,nil,"Weather")
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.windSettings")
|
||||
end)
|
||||
|
||||
|
||||
if SERVER then
|
||||
hook.Add("stormfox2.postlib", "stormfox2.svWindInit",function()
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
for _,ent in ipairs( StormFox2.Ent.env_winds ) do
|
||||
ent:SetKeyValue('windradius',-1) -- Make global
|
||||
ent:SetKeyValue('maxgustdelay', 20)
|
||||
ent:SetKeyValue('mingustdelay', 10)
|
||||
ent:SetKeyValue('gustduration', 5)
|
||||
end
|
||||
hook.Remove("stormfox2.postlib", "stormfox2.svWindInit")
|
||||
end)
|
||||
---Sets the wind force. Second argument is the lerp-time.
|
||||
---@param nForce number
|
||||
---@param nLerpTime? number
|
||||
---@server
|
||||
function StormFox2.Wind.SetForce( nForce, nLerpTime )
|
||||
StormFox2.Network.Set( "Wind", nForce, nLerpTime )
|
||||
end
|
||||
|
||||
---Sets the wind yaw. Second argument is the lerp-time.
|
||||
---@param nYaw number
|
||||
---@param nLerpTime? number
|
||||
---@server
|
||||
function StormFox2.Wind.SetYaw( nYaw, nLerpTime )
|
||||
StormFox2.Network.Set( "WindAngle", nYaw, nLerpTime )
|
||||
end
|
||||
end
|
||||
|
||||
---Returns the wind yaw-direction
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Wind.GetYaw()
|
||||
return StormFox2.Data.Get( "WindAngle", 0 )
|
||||
end
|
||||
|
||||
---Returns the wind force.
|
||||
---@return number
|
||||
---@shared
|
||||
function StormFox2.Wind.GetForce()
|
||||
return StormFox2.Data.Get( "Wind", 0 )
|
||||
end
|
||||
|
||||
-- Beaufort scale and Saffir–Simpson hurricane scale
|
||||
local bfs = {}
|
||||
bfs[0] = "sf_winddescription.calm"
|
||||
bfs[0.3] = "sf_winddescription.light_air"
|
||||
bfs[1.6] = "sf_winddescription.light_breeze"
|
||||
bfs[3.4] = "sf_winddescription.gentle_breeze"
|
||||
bfs[5.5] = "sf_winddescription.moderate_breeze"
|
||||
bfs[8] = "sf_winddescription.fresh_breeze"
|
||||
bfs[10.8] = "sf_winddescription.strong_breeze"
|
||||
bfs[13.9] = "sf_winddescription.near_gale"
|
||||
bfs[17.2] = "sf_winddescription.gale"
|
||||
bfs[20.8] = "sf_winddescription.strong_gale"
|
||||
bfs[24.5] = "sf_winddescription.storm"
|
||||
bfs[28.5] = "sf_winddescription.violent_storm"
|
||||
bfs[32.7] = "sf_winddescription.hurricane" -- Also known as cat 1
|
||||
bfs[43] = "sf_winddescription.cat2"
|
||||
bfs[50] = "sf_winddescription.cat3"
|
||||
bfs[58] = "sf_winddescription.cat4"
|
||||
bfs[70] = "sf_winddescription.cat5"
|
||||
local bfkey = table.GetKeys(bfs)
|
||||
table.sort(bfkey,function(a,b) return a < b end)
|
||||
|
||||
---Returns the current (or given wind in m/s), in a beaufort-scale and description.
|
||||
---@param ms? number
|
||||
---@return number
|
||||
---@return string
|
||||
---@shared
|
||||
function StormFox2.Wind.GetBeaufort(ms)
|
||||
local n = ms or StormFox2.Wind.GetForce()
|
||||
local Beaufort, Description = 0, "sf_winddescription.calm"
|
||||
for k,kms in ipairs( bfkey ) do
|
||||
if kms <= n then
|
||||
Beaufort, Description = k - 1, bfs[ kms ]
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return Beaufort, Description
|
||||
end
|
||||
-- Spawning env_wind won't work. Therefor we need to use the cl_tree_sway_dir on the client if it's not on the map.
|
||||
if CLIENT then
|
||||
local function updateWind()
|
||||
if StormFox2.Map.HadClass( "env_wind" ) then return end
|
||||
local nw = math.min(StormFox2.Wind.GetForce() * 0.6, 21)
|
||||
local ra = math.rad( StormFox2.Data.Get( "WindAngle", 0 ) )
|
||||
local wx,wy = math.cos(ra) * nw,math.sin(ra) * nw
|
||||
RunConsoleCommand("cl_tree_sway_dir",wx,wy)
|
||||
end
|
||||
hook.Add("StormFox2.Wind.Change","StormFox2.Wind.CLFix",function(windNorm, wind)
|
||||
if not StormFox2.Setting.GetCache("windmove_foliate", true) then return end
|
||||
updateWind()
|
||||
end)
|
||||
StormFox2.Setting.Callback("windmove_foliate", function(b)
|
||||
if b then
|
||||
updateWind()
|
||||
else
|
||||
RunConsoleCommand("cl_tree_sway_dir",0,0)
|
||||
end
|
||||
end)
|
||||
else
|
||||
local function updateWind(nw, ang)
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
local min = nw * .6
|
||||
local max = nw * .8
|
||||
local gust = math.min(nw, 5.5)
|
||||
for _,ent in ipairs( StormFox2.Ent.env_winds ) do
|
||||
if not IsValid(ent) then continue end
|
||||
--print(ent, max, min ,gust)
|
||||
if ang then ent:Fire('SetWindDir', ang) end
|
||||
ent:SetKeyValue('minwind', min)
|
||||
ent:SetKeyValue('maxwind', max)
|
||||
ent:SetKeyValue('gustdirchange', math.max(0, 21 - nw))
|
||||
ent:SetKeyValue('maxgust', gust)
|
||||
ent:SetKeyValue('mingust', gust * .8)
|
||||
end
|
||||
end
|
||||
hook.Add("StormFox2.Wind.Change","StormFox2.Wind.SVFix",function(windNorm, wind)
|
||||
local nw = StormFox2.Wind.GetForce() * 2
|
||||
local ang = StormFox2.Data.Get( "WindAngle", 0 )
|
||||
updateWind(nw, ang)
|
||||
end)
|
||||
StormFox2.Setting.Callback("windmove_foliate", function(b)
|
||||
if not StormFox2.Ent.env_winds then return end
|
||||
local ang = StormFox2.Data.Get( "WindAngle", 0 )
|
||||
if not b then
|
||||
updateWind(0, ang)
|
||||
return
|
||||
end
|
||||
local nw = StormFox2.Wind.GetForce() * 2
|
||||
updateWind(nw, ang)
|
||||
end)
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Calculate and update the wind direction
|
||||
---------------------------------------------------------------------------]]
|
||||
local windNorm = Vector(0,0,-1)
|
||||
local windVec = Vector(0,0,0)
|
||||
local wind,windAng = 0,-1
|
||||
local function calcfunc()
|
||||
local owind = StormFox2.Data.Get("Wind",0)
|
||||
local nwind = owind * 0.2
|
||||
local nang = StormFox2.Data.Get("WindAngle",0)
|
||||
if nwind == wind and nang == windAng then return end -- Nothing changed
|
||||
wind = nwind
|
||||
windAng = nang
|
||||
windNorm = Angle( 90 - sqrt(wind) * 10 ,windAng,0):Forward()
|
||||
windVec = windNorm * wind
|
||||
windNorm:Normalize()
|
||||
--[[<Shared>-----------------------------------------------------------------
|
||||
Gets called when the wind changes.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run("StormFox2.Wind.Change", windNorm, owind)
|
||||
end
|
||||
|
||||
-- If the wind-data changes, is changing or is done changing. Reclaculate the wind.
|
||||
timer.Create("StormFox2.Wind.Update", 1, 0, function()
|
||||
if not StormFox2.Data.IsLerping("Wind") and not StormFox2.Data.IsLerping("WindAngle") then return end
|
||||
calcfunc()
|
||||
end)
|
||||
local function dataCheck(sKey,sVar)
|
||||
if sKey ~= "Wind" and sKey ~= "WindAngle" then return end
|
||||
calcfunc()
|
||||
end
|
||||
hook.Add("StormFox2.data.change","StormFox2.Wind.Calc",dataCheck)
|
||||
hook.Add("StormFox2.data.lerpend", "StormFox2.Wind.Calcfinish", dataCheck)
|
||||
|
||||
---Returns the wind norm.
|
||||
---@return Vector
|
||||
---@shared
|
||||
function StormFox2.Wind.GetNorm()
|
||||
return windNorm
|
||||
end
|
||||
|
||||
---Returns the wind vector.
|
||||
---@return Vector
|
||||
---@shared
|
||||
function StormFox2.Wind.GetVector()
|
||||
return windVec
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Checks if an entity is out in the wind (or rain). Caches the result for 1 second.
|
||||
---------------------------------------------------------------------------]]
|
||||
local function IsMaterialEmpty( t )
|
||||
return t.HitTexture == "TOOLS/TOOLSINVISIBLE" or t.HitTexture == "**empty**" or t.HitTexture == "TOOLS/TOOLSNODRAW"
|
||||
end
|
||||
local function ET_II(pos, vec, mask, filter) -- Ignore invisble brushes 'n stuff'
|
||||
local lastT
|
||||
for i = 1, 5 do
|
||||
local t, a = ET(pos, vec, mask, filter)
|
||||
if not IsMaterialEmpty(t) and t.Hit then return t, a end
|
||||
lastT = lastT or t
|
||||
pos = t.HitPos
|
||||
end
|
||||
lastT.HitSky = true
|
||||
return lastT
|
||||
end
|
||||
local max_dis = 32400
|
||||
|
||||
---Checks to see if the entity is in the wind.
|
||||
---@param eEnt userdata
|
||||
---@param bDont_cache? boolean
|
||||
---@return boolean IsInWind
|
||||
---@return Vector WindNorm
|
||||
---@shared
|
||||
function StormFox2.Wind.IsEntityInWind(eEnt,bDont_cache)
|
||||
if not IsValid(eEnt) then return end
|
||||
if not bDont_cache then
|
||||
if eEnt.sf_wind_var and (eEnt.sf_wind_var[2] or 0) > CurTime() then
|
||||
return eEnt.sf_wind_var[1],windNorm
|
||||
else
|
||||
eEnt.sf_wind_var = {}
|
||||
end
|
||||
end
|
||||
local pos = eEnt:OBBCenter() + eEnt:GetPos()
|
||||
local tr = ET_II(pos, windNorm * -640000, MASK_SHOT, eEnt)
|
||||
local hitSky = tr.HitSky
|
||||
local dis = tr.HitPos:DistToSqr( pos )
|
||||
if not hitSky and dis >= max_dis then -- So far away. The wind would had gone around. Check if we're outside.
|
||||
local tr = ET(pos,Vector(0,0,640000),MASK_SHOT,eEnt)
|
||||
hitSky = tr.HitSky
|
||||
end
|
||||
if not bDont_cache then
|
||||
eEnt.sf_wind_var[1] = hitSky
|
||||
eEnt.sf_wind_var[2] = CurTime() + 1
|
||||
end
|
||||
return hitSky,windNorm
|
||||
end
|
||||
|
||||
-- Wind sounds
|
||||
if CLIENT then
|
||||
local windSnd = -1 -- -1 = none, 0 = outside, 0+ Distance to outside
|
||||
local windGusts = {}
|
||||
local maxVol = 1.5
|
||||
local function AddGuest( snd, vol, duration )
|
||||
if windGusts[snd] then return end
|
||||
if not duration then duration = SoundDuration( snd ) end
|
||||
windGusts[snd] = {vol * 0.4, CurTime() + duration - 1}
|
||||
end
|
||||
|
||||
timer.Create("StormFox2.Wind.Snd", 1, 0, function()
|
||||
windSnd = -1
|
||||
if StormFox2.Wind.GetForce() <= 0 then return end
|
||||
local env = StormFox2.Environment.Get()
|
||||
if not env or (not env.outside and not env.nearest_outside) then return end
|
||||
if not env.outside and env.nearest_outside then
|
||||
local view = StormFox2.util.RenderPos()
|
||||
windSnd = StormFox2.util.RenderPos():Distance(env.nearest_outside)
|
||||
else
|
||||
windSnd = 0
|
||||
end
|
||||
-- Guests
|
||||
local vM = (400 - windSnd) / 400
|
||||
if vM <= 0 then return end
|
||||
local wForce = StormFox2.Wind.GetForce()
|
||||
if math.random(50) > 40 then
|
||||
if wForce > 17 and math.random(1,2) > 1 then
|
||||
AddGuest("ambient/wind/windgust.wav",math.Rand(0.8, 1) * vM)
|
||||
elseif wForce > 14 and wForce < 30 then
|
||||
AddGuest("ambient/wind/wind_med" .. math.random(1,2) .. ".wav", math.min(maxVol, wForce / 30) * vM)
|
||||
end
|
||||
end
|
||||
if wForce > 27 and math.random(50) > 30 then
|
||||
AddGuest("ambient/wind/windgust_strong.wav",math.min(maxVol, wForce / 30) * vM)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cold "empty" wind: ambience/wind1.wav
|
||||
-- ambient/wind/wind1.wav
|
||||
-- ambient/wind/wind_rooftop1.wav
|
||||
-- ambient/wind/wind1.wav
|
||||
-- StormFox2.Ambience.ForcePlay
|
||||
hook.Add("StormFox2.Ambiences.OnSound", "StormFox2.Ambiences.Wind", function()
|
||||
if windSnd < 0 then return end -- No wind
|
||||
local wForce = StormFox2.Wind.GetForce() * 0.25
|
||||
local vM = (400 - windSnd) / 400
|
||||
if vM <= 0 then return end
|
||||
-- Main loop
|
||||
StormFox2.Ambience.ForcePlay( "ambient/wind/wind_rooftop1.wav", math.min((wForce - 1) / 35, maxVol) * vM * 0.8, math.min(1.2, 0.9 + wForce / 100) )
|
||||
-- Wind gusts
|
||||
for snd,data in pairs(windGusts) do
|
||||
if data[2] <= CurTime() then
|
||||
windGusts[snd] = nil
|
||||
else
|
||||
StormFox2.Ambience.ForcePlay( snd, 0.2 * data[1] + math.Rand(0, 0.1) )
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- Flag models
|
||||
local flags = {}
|
||||
local flag_models = {}
|
||||
flag_models["models/props_fairgrounds/fairgrounds_flagpole01.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole_american.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole_american_tattered.mdl"] = 90
|
||||
flag_models["models/props_street/flagpole.mdl"] = 90
|
||||
flag_models["models/mapmodels/flags.mdl"] = 0
|
||||
flag_models["models/props/de_cbble/cobble_flagpole.mdl"] = 180
|
||||
flag_models["models/props/de_cbble/cobble_flagpole_2.mdl"] = 225
|
||||
flag_models["models/props/props_gameplay/capture_flag.mdl"] = 270
|
||||
flag_models["models/props_medieval/pendant_flag/pendant_flag.mdl"] = 0
|
||||
flag_models["models/props_moon/parts/moon_flag.mdl"] = 0
|
||||
local function FlagInit()
|
||||
-- Check if there are any flags on the map
|
||||
for _,ent in pairs(ents.GetAll()) do
|
||||
if not ent:CreatedByMap() then continue end
|
||||
-- Check the angle
|
||||
if math.abs(ent:GetAngles():Forward():Dot(Vector(0,0,1))) > 5 then continue end
|
||||
if not flag_models[ent:GetModel()] then continue end
|
||||
table.insert(flags,ent)
|
||||
end
|
||||
if #flags > 0 then -- Only add the hook if there are flags on the map.
|
||||
hook.Add("StormFox2.data.change","StormFox2.flagcontroller",function(key,var)
|
||||
if key == "WindAngle" then
|
||||
--print("Windang", var)
|
||||
for _,ent in ipairs(flags) do
|
||||
if not IsValid(ent) then continue end
|
||||
local y = flag_models[ent:GetModel()] or 0
|
||||
ent:SetAngles(Angle(0,var + y,0))
|
||||
end
|
||||
elseif key == "Wind" then
|
||||
--print("Wind", var)
|
||||
for _,ent in ipairs(flags) do
|
||||
if not IsValid(ent) then continue end
|
||||
ent:SetPlaybackRate(math.Clamp(var / 7,0.5,10))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
hook.Add("StormFox2.PostEntityScan", "StormFox2.Wind.FlagInit", FlagInit)
|
||||
end
|
||||
|
||||
if CLIENT then return end
|
||||
-- Wind movment
|
||||
local function windMove(ply, mv, cmd )
|
||||
if not StormFox2.Setting.GetCache("windmove_players") then return end
|
||||
if( ply:GetMoveType() != MOVETYPE_WALK ) then return end
|
||||
local wF = (StormFox2.Wind.GetForce() - 15) / 11
|
||||
if wF <= 0 then return end
|
||||
if not StormFox2.Wind.IsEntityInWind(ply) then return end -- Not in wind
|
||||
-- Calc windforce
|
||||
local r = math.rad( StormFox2.Wind.GetYaw() - ply:GetAngles().y )
|
||||
local fS = math.cos( r ) * wF
|
||||
local sS = math.sin( r ) * wF
|
||||
|
||||
if mv:GetForwardSpeed() == 0 and mv:GetSideSpeed() == 0 then -- Not moving
|
||||
--mv:SetSideSpeed( - sS / 10) Annoying
|
||||
--mv:SetForwardSpeed( - fS / 10)
|
||||
else
|
||||
-- GetForwardMove() returns 10000y. We need to figure out the speed first.
|
||||
local running, walking = mv:KeyDown( IN_SPEED ), mv:KeyDown( IN_WALK )
|
||||
local speed = running and ply:GetRunSpeed() or walking and ply:GetSlowWalkSpeed() or ply:GetWalkSpeed()
|
||||
|
||||
local forward = math.Clamp(mv:GetForwardSpeed(), -speed, speed)
|
||||
local side = math.Clamp(mv:GetSideSpeed(), -speed, speed)
|
||||
if forward~=0 and side~=0 then
|
||||
forward = forward * .7
|
||||
side = side * .7
|
||||
end
|
||||
-- Now we modify them. We don't want to move back.
|
||||
if forward > 0 and fS < 0 then
|
||||
forward = math.max(0, forward / -fS)
|
||||
elseif forward < 0 and fS > 0 then
|
||||
forward = math.min(0, forward / fS)
|
||||
end
|
||||
if side > 0 and sS > 0 then
|
||||
side = math.max(0, side / sS)
|
||||
forward = forward + fS * 20
|
||||
elseif side < 0 and sS < 0 then
|
||||
side = math.min(0, side / -sS)
|
||||
forward = forward + fS * 20
|
||||
end
|
||||
-- Apply the new speed
|
||||
mv:SetForwardSpeed( forward )
|
||||
cmd:SetForwardMove( forward )
|
||||
mv:SetSideSpeed( side )
|
||||
cmd:SetSideMove( side )
|
||||
end
|
||||
end
|
||||
hook.Add("SetupMove", "windtest", windMove)
|
||||
|
||||
-- Wind proppush
|
||||
local move_list = {
|
||||
["rpg_missile"] = true,
|
||||
["npc_grenade_frag"] = true,
|
||||
["npc_grenade_bugbait"] = true, -- Doesn't work
|
||||
["gmod_hands"] = false,
|
||||
["gmod_tool"] = false
|
||||
}
|
||||
local function CanMoveClass( ent )
|
||||
if( IsValid( ent:GetParent()) ) then return end
|
||||
local class = ent:GetClass()
|
||||
if( move_list[class] == false ) then return false end
|
||||
return string.match(class,"^prop_") or string.match(class,"^gmod_") or move_list[class]
|
||||
end
|
||||
|
||||
local function ApplyWindEffect( ent, wind, windnorm )
|
||||
if ent:GetPersistent() then return end
|
||||
if(wind < 5) then return end
|
||||
-- Make a toggle
|
||||
local vol
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if(not phys or not IsValid(phys)) then -- No physics
|
||||
return
|
||||
end
|
||||
vol = phys:GetVolume() or 15
|
||||
-- Check Move
|
||||
local windPush = windnorm * 1.37 * vol * .66
|
||||
--windnorm * 5.92 * (vol / 50)
|
||||
local windRequ = phys:GetInertia()
|
||||
windRequ = max(windRequ.x,windRequ.y)
|
||||
if max(abs(windPush.x),abs(windPush.y)) < windRequ then -- Can't move
|
||||
return
|
||||
end
|
||||
local class = ent:GetClass()
|
||||
if( class != "npc_grenade_frag") then
|
||||
windPush.x = math.Clamp(windPush.x, -5500, 5500)
|
||||
windPush.y = math.Clamp(windPush.y, -5500, 5500)
|
||||
end
|
||||
-- Unfreeze
|
||||
if(wind > 20) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_unfreeze", true) ) then
|
||||
if not phys:IsMoveable() then
|
||||
phys:EnableMotion(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Unweld
|
||||
if(wind > 30) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_unweld", true) ) then
|
||||
if constraint.FindConstraint( ent, "Weld" ) and math.random(1, 15) < 2 then
|
||||
ent:EmitSound("physics/wood/wood_box_break" .. math.random(1,2) .. ".wav")
|
||||
constraint.RemoveConstraints( ent, "Weld" )
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Move
|
||||
phys:Wake()
|
||||
phys:ApplyForceCenter(Vector(windPush.x, windPush.y, math.max(phys:GetVelocity().z, 0)))
|
||||
-- Break
|
||||
if(wind > 40) then
|
||||
if( StormFox2.Setting.GetCache("windmove_props_break", true) ) then
|
||||
if not ent:IsVehicle() and (ent._sfnext_dmg or 0) <= CurTime() and ent:GetClass() != "npc_grenade_frag" then
|
||||
ent._sfnext_dmg = CurTime() + 0.5
|
||||
ent:TakeDamage(ent:Health() / 10 + 2,game.GetWorld(),game.GetWorld())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local move_tab = {}
|
||||
local current_prop = 0
|
||||
local function AddEntity( ent )
|
||||
if( ent._sfwindcan or 0 ) > CurTime() then return end
|
||||
if( StormFox2.Setting.GetCache("windmove_props_max", 50) <= table.Count(move_tab) ) then return end -- Too many props moving atm
|
||||
move_tab[ ent ] = CurTime()
|
||||
--ApplyWindEffect( ent, StormFox2.Wind.GetForce(), StormFox2.Wind.GetNorm() )
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated","StormFox.Wind.PropMove",function(ent)
|
||||
if( not StormFox2.Setting.GetCache("windmove_props", false) ) then return end
|
||||
if( not IsValid(ent) ) then return end
|
||||
if( not CanMoveClass( ent ) ) then return end
|
||||
AddEntity( ent )
|
||||
end)
|
||||
|
||||
local scanID = 0
|
||||
local function ScanProps()
|
||||
local t = ents.GetAll()
|
||||
if( #t < scanID) then
|
||||
scanID = 0
|
||||
end
|
||||
for i = scanID, math.min(#t, scanID + 30) do
|
||||
local ent = t[i]
|
||||
if(not IsValid( ent )) then break end
|
||||
if ent:GetPersistent() then continue end
|
||||
if not CanMoveClass( ent ) then continue end
|
||||
if move_tab[ ent ] then continue end -- Already added
|
||||
if not StormFox2.Wind.IsEntityInWind( ent ) then continue end -- Not in wind
|
||||
AddEntity( ent )
|
||||
end
|
||||
scanID = scanID + 30
|
||||
end
|
||||
|
||||
local next_loop = 0 -- We shouldn't run this on think if there arae too few props
|
||||
hook.Add("Think","StormFox.Wind.EffectProps",function()
|
||||
if( not StormFox2.Setting.GetCache("windmove_props", false) ) then return end
|
||||
if( next_loop > CurTime() ) then return end
|
||||
next_loop = CurTime() + (game.SinglePlayer() and 0.1 or 0.2)
|
||||
-- Scan on all entities. This would be slow. But we're only looking at 30 entities at a time.
|
||||
ScanProps()
|
||||
|
||||
local t = table.GetKeys( move_tab )
|
||||
if(#t < 1) then return end
|
||||
local wind = StormFox2.Wind.GetForce()
|
||||
-- Check if there is wind
|
||||
if( wind < 10) then
|
||||
table.Empty( move_tab )
|
||||
return
|
||||
end
|
||||
if( current_prop > #t) then
|
||||
current_prop = 1
|
||||
end
|
||||
local wind, c_windnorm = StormFox2.Wind.GetForce(), StormFox2.Wind.GetNorm()
|
||||
local windnorm = Vector(c_windnorm.x, c_windnorm.y, 0)
|
||||
local c = CurTime()
|
||||
for i = current_prop, math.min(#t, current_prop + 30) do
|
||||
local ent = t[i]
|
||||
if(not ent) then
|
||||
break
|
||||
end
|
||||
-- Check if valid
|
||||
if(not IsValid( ent ) or not StormFox2.Wind.IsEntityInWind( ent )) then
|
||||
move_tab[ent] = nil
|
||||
continue
|
||||
end
|
||||
-- Check if presistence
|
||||
if ent:GetPersistent() then continue end
|
||||
-- If the entity has been in the wind for over 10 seconds, try and move on and see if we can pick up something else
|
||||
if(move_tab[ ent ] < c - 10) then
|
||||
ent._sfwindcan = c + math.random(20, 30)
|
||||
if(StormFox2.Setting.GetCache("windmove_props_makedebris", true)) then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_DEBRIS )
|
||||
end
|
||||
move_tab[ent] = nil
|
||||
continue
|
||||
end
|
||||
ApplyWindEffect( ent, wind, windnorm )
|
||||
end
|
||||
current_prop = current_prop + 30
|
||||
end)
|
||||
37
lua/stormfox2/lib/sv_content.lua
Normal file
37
lua/stormfox2/lib/sv_content.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Adds SF content
|
||||
if not StormFox2.WorkShopVersion then
|
||||
local i = 0
|
||||
local function AddDir(dir,dirlen)
|
||||
if not dirlen then dirlen = dir:len() end
|
||||
local files, folders = file.Find(dir .. "/*", "GAME")
|
||||
for _, fdir in ipairs(folders) do
|
||||
if fdir ~= ".svn" then
|
||||
AddDir(dir .. "/" .. fdir)
|
||||
end
|
||||
end
|
||||
for k, v in ipairs(files) do
|
||||
local fil = dir .. "/" .. v
|
||||
resource.AddFile(fil)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
AddDir("materials/stormfox2")
|
||||
AddDir("sound/stormfox2")
|
||||
AddDir("models/stormfox2")
|
||||
StormFox2.Msg("Added " .. i .. " content files.")
|
||||
-- Add the workshop
|
||||
else
|
||||
resource.AddWorkshop(string.match(StormFox2.WorkShopURL, "%d+$"))
|
||||
StormFox2.Msg("Added content files from workshop.")
|
||||
end
|
||||
80
lua/stormfox2/lib/sv_entities.lua
Normal file
80
lua/stormfox2/lib/sv_entities.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Creates or finds map entities
|
||||
Hook:
|
||||
StormFox2.PostEntityScan Gets called after StormFox have located the map entities.
|
||||
Convar:
|
||||
sf_enable_mapsupport
|
||||
---------------------------------------------------------------------------]]
|
||||
StormFox2.Ent = {}
|
||||
CreateConVar("sf_enable_mapsupport","1",{FCVAR_REPLICATED,FCVAR_ARCHIVE},"StormFox2.setting.mapsupport")
|
||||
-- Find or creat entities
|
||||
local function GetOrCreate(str,only_get)
|
||||
local l = ents.FindByClass(str)
|
||||
local con = GetConVar("sf_enable_mapsupport")
|
||||
if #l > 0 then
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(55,255,55), "Found", Color( 255, 255, 255), "\n" )
|
||||
return l
|
||||
end
|
||||
if not con:GetBool() or only_get then -- Disabled mapsupport or don't create
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(255,55,55), "Not found", Color( 255, 255, 255), "\n" )
|
||||
return
|
||||
end
|
||||
local ent = ents.Create(str)
|
||||
ent:Spawn();
|
||||
ent:Activate();
|
||||
ent._sfcreated = true
|
||||
local s = string.rep(" ",24 - #str)
|
||||
MsgC( " ", Color(255,255,255), str, s, Color(155,155,255), "Created", Color( 255, 255, 255), "\n" )
|
||||
return {ent}
|
||||
end
|
||||
-- We need to use this function, as some entities spawn regardless of what the map has.
|
||||
local function findEntities()
|
||||
StormFox2.Msg( "Scanning mapentities ..." )
|
||||
local tSunlist = ents.FindByClass( "env_sun" )
|
||||
for i = 1, #tSunlist do -- Remove any env_suns, there should be only one but who knows
|
||||
tSunlist[ i ]:Fire( "TurnOff" )
|
||||
end
|
||||
StormFox2.Ent.env_skypaints = GetOrCreate( "env_skypaint" )
|
||||
StormFox2.Ent.light_environments = GetOrCreate( "light_environment", true)
|
||||
StormFox2.Ent.env_fog_controllers = GetOrCreate( "env_fog_controller" )
|
||||
StormFox2.Ent.shadow_controls = GetOrCreate( "shadow_control", true )
|
||||
StormFox2.Ent.env_tonemap_controllers = GetOrCreate("env_tonemap_controller", true )
|
||||
StormFox2.Ent.env_winds = GetOrCreate("env_wind", true ) -- Can't spawn the wind controller without problems
|
||||
StormFox2.Ent.env_tonemap_controller = GetOrCreate( "env_tonemap_controller", true)
|
||||
-- Kill TF2 sun
|
||||
for k,v in ipairs(ents.FindByModel("models/props_skybox/sunnoon.mdl")) do
|
||||
if v:IsValid() then
|
||||
v:SetNoDraw( true )
|
||||
end
|
||||
end
|
||||
--[[-------------------------------------------------------------------------
|
||||
Gets called when StormFox has handled map-entities.
|
||||
---------------------------------------------------------------------------]]
|
||||
hook.Run( "StormFox2.PostEntityScan" )
|
||||
end
|
||||
-- If this is first run, wait for InitPostEntity.
|
||||
hook.Add("StormFox2.InitPostEntity","StormFox2.Entities",findEntities)
|
||||
-- Tell clients about explosions
|
||||
util.AddNetworkString("StormFox2.entity.explosion")
|
||||
hook.Add("EntityRemoved","StormFox2.Entitys.Explosion",function(ent)
|
||||
if ent:GetClass() ~= "env_explosion" then return end
|
||||
local t = ent:GetKeyValues()
|
||||
net.Start("StormFox2.entity.explosion")
|
||||
net.WriteVector(ent:GetPos())
|
||||
net.WriteUInt(t.iRadiusOverride or t.iMagnitude, 16)
|
||||
net.WriteUInt(t.iMagnitude, 16)
|
||||
net.SendPVS(ent:GetPos())
|
||||
hook.Run("StormFox2.Entitys.OnExplosion", ent:GetPos(), t.iRadiusOverride, t.iMagnitude)
|
||||
end)
|
||||
139
lua/stormfox2/lib/sv_perlin.lua
Normal file
139
lua/stormfox2/lib/sv_perlin.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
Implemented as described here:
|
||||
http://flafla2.github.io/2014/08/09/perlinnoise.html
|
||||
|
||||
Copied from: https://gist.github.com/SilentSpike/25758d37f8e3872e1636d90ad41fe2ed
|
||||
]]--
|
||||
|
||||
local floor,band,clamp,max = math.floor,bit.band,math.Clamp,math.max
|
||||
|
||||
perlin = {}
|
||||
local p = {}
|
||||
|
||||
-- Hash lookup table as defined by Ken Perlin
|
||||
-- This is a randomly arranged array of all numbers from 0-255 inclusive
|
||||
local permutation = {151,160,137,91,90,15,
|
||||
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
|
||||
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
|
||||
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
|
||||
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
|
||||
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
|
||||
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
|
||||
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
|
||||
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
|
||||
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
|
||||
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
|
||||
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||
}
|
||||
-- p is used to hash unit cube coordinates to [0, 255]
|
||||
for i = 0,255 do
|
||||
-- Convert to 0 based index table
|
||||
p[i] = permutation[i + 1]
|
||||
-- Repeat the array to avoid buffer overflow in hash function
|
||||
p[i + 256] = permutation[i + 1]
|
||||
end
|
||||
-- Functions
|
||||
local dot_product = {
|
||||
[0x0] = function(x,y,z) return x + y end,
|
||||
[0x1] = function(x,y,z) return -x + y end,
|
||||
[0x2] = function(x,y,z) return x - y end,
|
||||
[0x3] = function(x,y,z) return -x - y end,
|
||||
[0x4] = function(x,y,z) return x + z end,
|
||||
[0x5] = function(x,y,z) return -x + z end,
|
||||
[0x6] = function(x,y,z) return x - z end,
|
||||
[0x7] = function(x,y,z) return -x - z end,
|
||||
[0x8] = function(x,y,z) return y + z end,
|
||||
[0x9] = function(x,y,z) return -y + z end,
|
||||
[0xA] = function(x,y,z) return y - z end,
|
||||
[0xB] = function(x,y,z) return -y - z end,
|
||||
[0xC] = function(x,y,z) return y + x end,
|
||||
[0xD] = function(x,y,z) return -y + z end,
|
||||
[0xE] = function(x,y,z) return y - x end,
|
||||
[0xF] = function(x,y,z) return -y - z end
|
||||
}
|
||||
local function grad(hash, x, y, z)
|
||||
return dot_product[band(hash,0xF)](x,y,z)
|
||||
end
|
||||
local function fade(t)
|
||||
return t * t * t * (t * (t * 6 - 15) + 10)
|
||||
end
|
||||
local function lerp(t, a, b)
|
||||
return a + t * (b - a)
|
||||
end
|
||||
function perlin.noise(x, y, z, zoom) -- [-1 , 1]
|
||||
zoom = zoom or 100
|
||||
x = x / zoom
|
||||
y = y and y / zoom or 0
|
||||
z = z and z / zoom or 0
|
||||
|
||||
-- Calculate the "unit cube" that the point asked will be located in
|
||||
local xi = floor(x) % 256
|
||||
local yi = floor(y) % 256
|
||||
local zi = floor(z) % 256
|
||||
|
||||
-- Next we calculate the location (from 0 to 1) in that cube
|
||||
x = x - floor(x)
|
||||
y = y - floor(y)
|
||||
z = z - floor(z)
|
||||
|
||||
-- We also fade the location to smooth the result
|
||||
local u = fade(x)
|
||||
local v = fade(y)
|
||||
local w = fade(z)
|
||||
|
||||
-- Hash all 8 unit cube coordinates surrounding input coordinate
|
||||
local A = p[xi ] + yi
|
||||
local AA = p[A ] + zi
|
||||
local AB = p[A + 1 ] + zi
|
||||
local AAA = p[ AA ]
|
||||
local ABA = p[ AB ]
|
||||
local AAB = p[ AA + 1 ]
|
||||
local ABB = p[ AB + 1 ]
|
||||
|
||||
local B = p[xi + 1] + yi
|
||||
local BA = p[B ] + zi
|
||||
local BB = p[B + 1 ] + zi
|
||||
local BAA = p[ BA ]
|
||||
local BBA = p[ BB ]
|
||||
local BAB = p[ BA + 1 ]
|
||||
local BBB = p[ BB + 1 ]
|
||||
|
||||
-- Take the weighted average between all 8 unit cube coordinates
|
||||
return lerp(w,
|
||||
lerp(v,
|
||||
lerp(u,
|
||||
grad(AAA,x,y,z),
|
||||
grad(BAA,x-1,y,z)
|
||||
),
|
||||
lerp(u,
|
||||
grad(ABA,x,y-1,z),
|
||||
grad(BBA,x-1,y-1,z)
|
||||
)
|
||||
),
|
||||
lerp(v,
|
||||
lerp(u,
|
||||
grad(AAB,x,y,z-1), grad(BAB,x-1,y,z-1)
|
||||
),
|
||||
lerp(u,
|
||||
grad(ABB,x,y-1,z-1), grad(BBB,x-1,y-1,z-1)
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
function perlin.range(x, y ,z, zoom) -- [0 - 1]
|
||||
return (1 + perlinnoise(x, y, z, zoom)) / 2
|
||||
end
|
||||
function perlin.rangeSub(x, y, z , zoom, n)
|
||||
return max(0,(perlin.range(x, y ,z, zoom) - n ) / (1 - n))
|
||||
end
|
||||
Reference in New Issue
Block a user