dobrograd-13-06-2022/garrysmod/addons/util-dlib/lua/dlib/extensions/extensions.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

699 lines
17 KiB
Lua

--
-- Copyright (C) 2017-2020 DBotThePony
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:
-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.
local PhysObj = FindMetaTable('PhysObj')
local vectorMeta = FindMetaTable('Vector')
local vehicleMeta = FindMetaTable('Vehicle')
local entMeta = FindMetaTable('Entity')
local Color = Color
local math = math
local ipairs = ipairs
local assert = assert
local select = select
local language = language
local list = list
local pairs = pairs
local CLIENT = CLIENT
_G.angle_empty = Angle()
_G.angle_up = Angle(90)
_G.angle_down = Angle(-90)
_G.angle_left = Angle(0, 90)
_G.angle_right = Angle(0, -90)
function PhysObj:SetAngleVelocity(newAngle)
return self:AddAngleVelocity(newAngle - self:GetAngleVelocity())
end
PhysObj.DLibSetMass = PhysObj.DLibSetMass or PhysObj.SetMass
PhysObj.DLibEnableCollisions = PhysObj.DLibEnableCollisions or PhysObj.EnableCollisions
PhysObj.DLibEnableDrag = PhysObj.DLibEnableDrag or PhysObj.EnableDrag
PhysObj.DLibEnableMotion = PhysObj.DLibEnableMotion or PhysObj.EnableMotion
PhysObj.DLibEnableGravity = PhysObj.DLibEnableGravity or PhysObj.EnableGravity
function PhysObj:SetMass(newMass)
if newMass <= 0 then
print(debug.traceback('Mass can not be lower or equal to 0!', 2))
return
end
return self:DLibSetMass(newMass)
end
local worldspawn, worldspawnPhys
-- shut up dumb addons
function PhysObj:EnableCollisions(newStatus)
worldspawn = worldspawn or Entity(0)
worldspawnPhys = worldspawnPhys or worldspawn:GetPhysicsObject()
if worldspawnPhys == self then
print(debug.traceback('Attempt to call :EnableCollisions() on World PhysObj!', 2))
return
end
return self:DLibEnableCollisions(newStatus)
end
--[[
@doc
@fname Vector:Copy
@desc
Same as doing `Vector(self)`
@enddesc
@returns
Vector
]]
function vectorMeta:Copy()
return Vector(self)
end
--[[
@doc
@fname Vector:__call
@returns
Vector: copy
]]
function vectorMeta:__call()
return Vector(self)
end
--[[
@doc
@fname Vector:ToNative
@returns
Vector: self
]]
function vectorMeta:ToNative()
return self
end
--[[
@doc
@fname Vector:IsNormalized
@returns
boolean
]]
function vectorMeta:IsNormalized()
return self.x <= 1 and self.y <= 1 and self.z <= 1 and self.x >= -1 and self.y >= -1 and self.z >= -1
end
--[[
@doc
@fname Vector:Receive
@args Vector from
@returns
Vector: self
]]
function vectorMeta:Receive(target)
local x, y, z = target.x, target.y, target.z
self.x, self.y, self.z = x, y, z
return self
end
--[[
@doc
@fname Vector:RotateAroundAxis
@args Vector axis, number rotation
@returns
Vector: self
]]
function vectorMeta:RotateAroundAxis(axis, rotation)
local ang = self:Angle()
ang:RotateAroundAxis(axis, rotation)
return self:Receive(ang:Forward() * self:Length())
end
--[[
@doc
@fname Vector:ToColor
@returns
Color
]]
function vectorMeta:ToColor()
return Color(self.x * 255, self.y * 255, self.z * 255)
end
local type = luatype
--[[
@doc
@fname Vector:WithinAABox
@args Vector mins, Vector maxs
@desc
can also accept `LVector`
@enddesc
@returns
boolean
]]
function vectorMeta:WithinAABox(mins, maxs)
if type(mins) ~= 'Vector' and type(mins) ~= 'LVector' then
error('Vector:WithinAABox(' .. type(mins) .. ', ' .. type(maxs) .. ') - invalid call')
end
if type(maxs) ~= 'Vector' and type(maxs) ~= 'LVector' then
error('Vector:WithinAABox(' .. type(mins) .. ', ' .. type(maxs) .. ') - invalid call')
end
return self.x >= mins.x
and self.y >= mins.y
and self.z >= mins.z
and self.x <= maxs.x
and self.y <= maxs.y
and self.z <= maxs.z
end
--[[
@doc
@fname sql.EQuery
@args string query
@desc
Same as gmod's sql.Query except it prints errors in console when one occures
@enddesc
@returns
any: returned value from database
]]
function sql.EQuery(...)
local data = sql.Query(...)
if data == false then
DLib.Message('SQL: ', ...)
DLib.Message(sql.LastError())
end
return data
end
--[[
@doc
@fname math.progression
@args number self, number min, number max, number middle = nil
@returns
number: position of self between min and max in 0-1 range, or 0-1-0 is middle is not nil
]]
function math.progression(self, min, max, middle)
if self < min then return 0 end
if middle then
if self < min or self >= max then return 0 end
if self < middle then
return math.min((self - min) / (middle - min), 1)
elseif self > middle then
return 1 - math.min((self - middle) / (max - middle), 1)
elseif self == middle then
return 1
end
end
return math.min((self - min) / (max - min), 1)
end
--[[
@doc
@fname math.equal
@args vararg numbers
@returns
boolean
]]
function math.equal(...)
local amount = select('#', ...)
assert(amount > 1, 'At least two numbers are required!')
local lastValue
for i = 1, amount do
local value = select(i, ...)
lastValue = lastValue or value
if value ~= lastValue then return false end
end
return true
end
--[[
@doc
@fname math.average
@args vararg numbers
@returns
number: the average
]]
function math.average(...)
local amount = select('#', ...)
assert(amount > 1, 'At least two numbers are required!')
local total = 0
for i = 1, amount do
total = total + select(i, ...)
end
return total / amount
end
local type = type
local table = table
local unpack = unpack
local buffer = {}
local function tbezier(t, values, amount)
assert(type(t) == 'number', 'invalid T variable')
assert(t >= 0 and t <= 1, '0 <= t <= 1!')
assert(#values >= 2, 'at least two values must be provided')
local a, b = values[1], values[2]
-- linear
if amount == 2 then
return a + (b - a) * t
-- square
elseif amount == 3 then
return (1 - t):pow(2) * a + 2 * t * (1 - t) * b + t:pow(2) * values[3]
-- cube
elseif amount == 4 then
return (1 - t):pow(3) * a + 3 * t * (1 - t):pow(2) * b + 3 * t:pow(2) * (1 - t) * values[3] + t:pow(3) * values[4]
end
for point = 1, amount do
local point1 = values[point]
local point2 = values[point + 1]
if not point2 then break end
buffer[point] = point1 + (point2 - point1) * t
end
return tbezier(t, buffer, amount - 1)
end
--[[
@doc
@fname math.bezier
@args number t, vararg numbers
@returns
number
]]
function math.bezier(t, ...)
return tbezier(t, {...}, select('#', ...))
end
--[[
@doc
@fname math.tbezier
@args number t, table numbers
@returns
number
]]
-- accepts table
function math.tbezier(t, values)
return tbezier(t, values, #values)
end
--[[
@doc
@fname math.tformat
@args number time
@returns
table: formatted table
]]
function math.tformat(time)
local centuries, years, months, weeks, days, hours, minutes, seconds = math.tformatVararg(time)
return {
centuries = centuries, years = years, months = months, weeks = weeks, days = days, hours = hours, minutes = minutes, seconds = seconds
}
end
--[[
@doc
@fname math.tformatVararg
@args number time
@returns
number: centuries
number: years
number: months
number: weeks
number: days
number: hours
number: minutes
number: seconds
]]
function math.tformatVararg(time)
assert(type(time) == 'number', 'Invalid time provided.')
if time > 0xFFFFFFFFFF then
error('Value is too big! Maximum is ' .. 0xFFFFFFFFFF)
elseif time <= 1 then
return 0, 0, 0, 0, 0, 0, 0, 0
end
local centuries = (time - time % 0xBBF81E00) / 0xBBF81E00
time = time - centuries * 0xBBF81E00
local years = (time - time % 0x01E13380) / 0x01E13380
time = time - years * 0x01E13380
local months = ((time - time % 0x00278D00) / 0x00278D00):min(11)
time = time - months * 0x00278D00
local weeks = (time - time % 604800) / 604800
time = time - weeks * 604800
local days = (time - time % 86400) / 86400
time = time - days * 86400
local hours = (time - time % 3600) / 3600
time = time - hours * 3600
local minutes = (time - time % 60) / 60
time = time - minutes * 60
local seconds = time:floor()
return centuries, years, months, weeks, days, hours, minutes, seconds
end
--[[
@doc
@fname math.untformat
@args table time
@desc
reverse of table format of number
@enddesc
@returns
number
]]
function math.untformat(time)
assert(type(time) == 'table', 'Invalid time provided. You must provide table in math.tformat output format.')
return math.untformatVararg(time.centuries, time.years, time.months, time.weeks, time.days, time.hours, time.minutes, time.seconds)
end
--[[
@doc
@fname math.untformatVararg
@args number centuries, number years, number months, number weeks, number days, number hours, number minutes, number seconds
@desc
reverse of time numbers format of number
@enddesc
@returns
number
]]
function math.untformatVararg(centuries, years, months, weeks, days, hours, minutes, seconds)
assert(type(centuries) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(years) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(months) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(weeks) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(days) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(hours) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(minutes) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
assert(type(seconds) == 'number', 'Invalid time provided. You must provide table in math.tformat output format.')
return centuries * 0xBBF81E00
+ years * 0x01E13380
+ months * 0x00278D00
+ weeks * 604800
+ days * 86400
+ hours * 3600
+ minutes * 60
+ seconds
end
do
local daysIn = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
local _timeCached = {}
local _timeCachedHigh = {}
for month = 1, 12 do
local stamp1 = 0
local stamp2 = 0
for _month = 1, month - 1 do
if _month == 2 then
stamp1 = stamp1 + 29 * 86400
stamp2 = stamp2 + 28 * 86400
else
stamp1 = stamp1 + daysIn[_month] * 86400
stamp2 = stamp2 + daysIn[_month] * 86400
end
end
_timeCachedHigh[month] = stamp1
_timeCached[month] = stamp2
end
local yearLength = 31536000
local longYearLength = 31622400
local isnumber = isnumber
function math.dateToTimestamp(year, month, day, hour, minute, second)
assert(isnumber(year), 'year is not a number or missing')
assert(isnumber(month), 'month is not a number or missing')
assert(isnumber(day), 'day is not a number or missing')
assert(isnumber(hour), 'hour is not a number or missing')
assert(isnumber(minute), 'minute is not a number or missing')
assert(isnumber(second), 'second is not a number or missing')
assert(year >= 1970, 'year < 1970 is not supported yet')
assert(month >= 1 and month < 13, 'invalid month')
assert(day >= 1 and day <= 31, 'invalid day')
assert(hour >= 0 and hour <= 24, 'invalid hour')
assert(minute >= 0 and minute <= 60, 'invalid minute')
assert(second >= 0 and second <= 60, 'invalid second')
local yearS, peaks, skips, shift = year - 1970, 0, 0, 0
if year > 2000 then
-- ???
peaks = ((year - 1600) / 400):floor()
yearS = yearS - peaks
shift = -86400 * peaks
skips = (((year - 1900) / 100):floor() - peaks):max(0)
yearS = yearS - skips
local peak2 = (((year - 1968) / 4):floor() - skips):max(0)
yearS = yearS - peak2
peaks = peaks + peak2
elseif year < 2000 then
peaks = ((year - 1968) / 4):floor()
yearS = yearS - peaks
else
peaks = 7
yearS = yearS - 7
end
return peaks * longYearLength + skips * yearLength + yearS * yearLength +
((year % 400 == 0 or year % 100 ~= 0 and year % 4 == 0) and _timeCachedHigh[month] or _timeCached[month]) +
(day - 1) * 86400 + hour * 3600 + minute * 60 + second + shift
end
end
local CLIENT = CLIENT
local hook = hook
local net = net
if SERVER then
net.pool('dlib.limithitfix')
end
local plyMeta = FindMetaTable('Player')
--[[
@doc
@fname Player:LimitHit
@desc
This function no longer produce limit hit message when called clientside
this function is internal and is used by gmod itself
but the override allows you to put !g:Player:CheckLimit in shared code of toolguns
@enddesc
]]
function plyMeta:LimitHit(limit)
-- we call CheckLimit() on client just for prediction
-- so when we actually hit limit - it can produce two messages because client will also try to
-- display this message by calling hook LimitHit. So, let's call that only once.
-- if you want to call this function clientside despite this text and warning
-- you can run hooks on LimitHit manually by doing so:
-- hook.Run('LimitHit', 'mylimit')
-- you shouldn't really call this function directly clientside
if CLIENT then return end
net.Start('dlib.limithitfix')
net.WriteString(limit)
net.Send(self)
end
if CLIENT then
net.receive('dlib.limithitfix', function()
hook.Run('LimitHit', net.ReadString())
end)
end
if CLIENT then
local surface = surface
surface._DLibPlaySound = surface._DLibPlaySound or surface.PlaySound
function surface.PlaySound(path, ...)
assert(type(path) == 'string', 'surface.PlaySound - string expected, got ' .. type(path))
local can = hook.Run('SurfaceEmitSound', path, ...)
if can == false then return end
return surface._DLibPlaySound(path, ...)
end
-- cache and speedup lookups a bit
local use_type = CreateConVar('dlib_screenscale', '1', {FCVAR_ARCHIVE}, 'Use screen height as screen scale parameter instead of screen width')
local dlib_screenscale_mul = CreateConVar('dlib_screenscale_mul', '1', {FCVAR_ARCHIVE}, 'GUI Scale multiplier')
DLib.dlib_screenscale_mul = dlib_screenscale_mul
local dlib_screenscale_mul_get = dlib_screenscale_mul:GetFloat(1):max(0)
local ScrWL = ScrWL
local ScrHL = ScrHL
local screenfunc
if use_type:GetBool() then
function screenfunc(modify)
return ScrHL() / 480 * modify * dlib_screenscale_mul_get
end
else
function screenfunc(modify)
return ScrWL() / 640 * modify * dlib_screenscale_mul_get
end
end
--[[
@doc
@fname ScreenSize
@args number modify
@desc
same as ScreenScale but use screen height (by default)
behvaior can be changed by user
@enddesc
@returns
number
]]
function _G.ScreenSize(modify)
return screenfunc(modify)
end
local function dlib_screenscale_chages()
if use_type:GetBool() then
function screenfunc(modify)
return ScrHL() / 480 * modify
end
else
function screenfunc(modify)
return ScrWL() / 640 * modify
end
end
DLib.TriggerScreenSizeUpdate(ScrWL(), ScrHL(), ScrWL(), ScrHL())
end
cvars.AddChangeCallback('dlib_screenscale', dlib_screenscale_chages, 'DLib')
cvars.AddChangeCallback('dlib_screenscale_mul', function()
dlib_screenscale_mul_get = dlib_screenscale_mul:GetFloat(1):max(0)
DLib.TriggerScreenSizeUpdate(ScrWL(), ScrHL(), ScrWL(), ScrHL())
end, 'DLib')
end
local threads1T = {}
local threads1C = {}
local threads2T = {}
local threads2C = {}
local SysTime = SysTime
local coroutine = coroutine
local maximal = 0
--[[
@doc
@fname coroutine.syswait
@args number seconds
@desc
like !g:coroutine.wait but use `SysTime()`
@enddesc
]]
function coroutine.syswait(seconds)
if type(seconds) ~= 'number'then
error('Invalid seconds amount provided')
end
local thread = assert(coroutine.running(), 'Not inside coroutine!')
if seconds < 0 then return end
table.insert(threads1C, thread)
table.insert(threads1T, SysTime() + seconds)
coroutine.yield()
end
hook.Add(SERVER and 'Tick' or 'Think', 'DLib.coroutine.syswait', function()
local stime = SysTime()
for i = 1, #threads2C do
local thread = threads2C[i]
local time = threads2T[i]
if time and time <= stime then
threads2C[i] = nil
threads2T[i] = nil
if coroutine.status(thread) == 'suspended' then
local status, err = coroutine.resume(thread)
if not status then
DLib.MessageError('Error executing thread:')
ErrorNoHalt(err .. '\n')
end
end
end
end
local sw1t, sw2t, sw1c, sw2c = threads1T, threads2T, threads1C, threads2C
threads2T = sw1t
threads1T = sw2t
threads2C = sw1c
threads1C = sw2c
end, 3)