Files
wnsrc/gamemodes/terrortown/gamemode/util.lua

380 lines
9.3 KiB
Lua
Raw Normal View History

2024-08-04 22:55:00 +03:00
--[[
| 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/
--]]
-- Random stuff
if not util then return end
local math = math
local string = string
local table = table
local pairs = pairs
-- attempts to get the weapon used from a DamageInfo instance needed because the
-- GetAmmoType value is useless and inflictor isn't properly set (yet)
function util.WeaponFromDamage(dmg)
local inf = dmg:GetInflictor()
local wep = nil
if IsValid(inf) then
if inf:IsWeapon() or inf.Projectile then
wep = inf
elseif dmg:IsDamageType(DMG_DIRECT) or dmg:IsDamageType(DMG_CRUSH) then
-- DMG_DIRECT is the player burning, no weapon involved
-- DMG_CRUSH is physics or falling on someone
wep = nil
elseif inf:IsPlayer() then
wep = inf:GetActiveWeapon()
if not IsValid(wep) then
-- this may have been a dying shot, in which case we need a
-- workaround to find the weapon because it was dropped on death
wep = IsValid(inf.dying_wep) and inf.dying_wep or nil
end
end
end
return wep
end
-- Gets the table for a SWEP or a weapon-SENT (throwing knife), so not
-- equivalent to weapons.Get. Do not modify the table returned by this, consider
-- as read-only.
function util.WeaponForClass(cls)
local wep = weapons.GetStored(cls)
if not wep then
wep = scripted_ents.GetStored(cls)
if wep then
-- don't like to rely on this, but the alternative is
-- scripted_ents.Get which does a full table copy, so only do
-- that as last resort
wep = wep.t or scripted_ents.Get(cls)
end
end
return wep
end
function util.GetAlivePlayers()
local alive = {}
for k, p in player.Iterator() do
if IsValid(p) and p:Alive() and p:IsTerror() then
table.insert(alive, p)
end
end
return alive
end
function util.GetNextAlivePlayer(ply)
local alive = util.GetAlivePlayers()
if #alive < 1 then return nil end
local prev = nil
local choice = nil
if IsValid(ply) then
for k,p in ipairs(alive) do
if prev == ply then
choice = p
end
prev = p
end
end
if not IsValid(choice) then
choice = alive[1]
end
return choice
end
-- Uppercases the first character only
function string.Capitalize(str)
return string.upper(string.sub(str, 1, 1)) .. string.sub(str, 2)
end
util.Capitalize = string.Capitalize
-- Color unpacking
function clr(color) return color.r, color.g, color.b, color.a; end
if CLIENT then
-- Is screenpos on screen?
function IsOffScreen(scrpos)
return not scrpos.visible or scrpos.x < 0 or scrpos.y < 0 or scrpos.x > ScrW() or scrpos.y > ScrH()
end
end
function AccessorFuncDT(tbl, varname, name)
tbl["Get" .. name] = function(s) return s.dt and s.dt[varname] end
tbl["Set" .. name] = function(s, v) if s.dt then s.dt[varname] = v end end
end
function util.PaintDown(start, effname, ignore)
local btr = util.TraceLine({start=start, endpos=(start + Vector(0,0,-256)), filter=ignore, mask=MASK_SOLID})
util.Decal(effname, btr.HitPos+btr.HitNormal, btr.HitPos-btr.HitNormal)
end
local function DoBleed(ent)
if not IsValid(ent) or (ent:IsPlayer() and (not ent:Alive() or not ent:IsTerror())) then
return
end
local jitter = VectorRand() * 30
jitter.z = 20
util.PaintDown(ent:GetPos() + jitter, "Blood", ent)
end
-- Something hurt us, start bleeding for a bit depending on the amount
function util.StartBleeding(ent, dmg, t)
if dmg < 5 or not IsValid(ent) then
return
end
if ent:IsPlayer() and (not ent:Alive() or not ent:IsTerror()) then
return
end
local times = math.Clamp(math.Round(dmg / 15), 1, 20)
local delay = math.Clamp(t / times , 0.1, 2)
if ent:IsPlayer() then
times = times * 2
delay = delay / 2
end
timer.Create("bleed" .. ent:EntIndex(), delay, times,
function() DoBleed(ent) end)
end
function util.StopBleeding(ent)
timer.Remove("bleed" .. ent:EntIndex())
end
local zapsound = Sound("npc/assassin/ball_zap1.wav")
function util.EquipmentDestroyed(pos)
local effect = EffectData()
effect:SetOrigin(pos)
util.Effect("cball_explode", effect)
sound.Play(zapsound, pos)
end
-- Useful default behaviour for semi-modal DFrames
function util.BasicKeyHandler(pnl, kc)
-- passthrough F5
if kc == KEY_F5 then
RunConsoleCommand("jpeg")
else
pnl:Close()
end
end
function util.noop() end
function util.passthrough(x) return x end
-- Fisher-Yates shuffle
local rand = math.random
function table.Shuffle(t)
local n = #t
while n > 1 do
-- n is now the last pertinent index
local k = rand(n) -- 1 <= k <= n
-- Quick swap
t[n], t[k] = t[k], t[n]
n = n - 1
end
return t
end
-- Override with nil check
function table.HasValue(tbl, val)
if not tbl then return end
for k, v in pairs(tbl) do
if v == val then return true end
end
return false
end
-- Value equality for tables
function table.EqualValues(a, b)
if a == b then return true end
for k, v in pairs(a) do
if v != b[k] then
return false
end
end
return true
end
-- Basic table.HasValue pointer checks are insufficient when checking a table of
-- tables, so this uses table.EqualValues instead.
function table.HasTable(tbl, needle)
if not tbl then return end
for k, v in pairs(tbl) do
if v == needle then
return true
elseif table.EqualValues(v, needle) then
return true
end
end
return false
end
-- Returns copy of table with only specific keys copied
function table.CopyKeys(tbl, keys)
if not (tbl and keys) then return end
local out = {}
local val = nil
for _, k in pairs(keys) do
val = tbl[k]
if istable(val) then
out[k] = table.Copy(val)
else
out[k] = val
end
end
return out
end
local gsub = string.gsub
-- Simple string interpolation:
-- string.Interp("{killer} killed {victim}", {killer = "Bob", victim = "Joe"})
-- returns "Bob killed Joe"
-- No spaces or special chars in parameter name, just alphanumerics.
function string.Interp(str, tbl)
return gsub(str, '{(%w+)}', tbl)
end
-- Short helper for input.LookupBinding, returns capitalised key or a default
function Key(binding, default)
local b = input.LookupBinding(binding)
if not b then return default end
return string.upper(b)
end
local exp = math.exp
-- Equivalent to ExponentialDecay from Source's mathlib.
-- Convenient for falloff curves.
function math.ExponentialDecay(halflife, dt)
-- ln(0.5) = -0.69..
return exp((-0.69314718 / halflife) * dt)
end
function Dev(level, ...)
if cvars and cvars.Number("developer", 0) >= level then
Msg("[TTT dev]")
-- table.concat does not tostring, derp
local params = {...}
for i=1,#params do
Msg(" " .. tostring(params[i]))
end
Msg("\n")
end
end
function IsPlayer(ent)
return ent and ent:IsValid() and ent:IsPlayer()
end
function IsRagdoll(ent)
return ent and ent:IsValid() and ent:GetClass() == "prop_ragdoll"
end
local band = bit.band
function util.BitSet(val, bit)
return band(val, bit) == bit
end
if CLIENT then
local healthcolors = {
healthy = Color(0, 255, 0, 255),
hurt = Color(170, 230, 10, 255),
wounded = Color(230, 215, 10, 255),
badwound= Color(255, 140, 0, 255),
death = Color(255, 0, 0, 255)
};
function util.HealthToString(health, maxhealth)
maxhealth = maxhealth or 100
if health > maxhealth * 0.9 then
return "hp_healthy", healthcolors.healthy
elseif health > maxhealth * 0.7 then
return "hp_hurt", healthcolors.hurt
elseif health > maxhealth * 0.45 then
return "hp_wounded", healthcolors.wounded
elseif health > maxhealth * 0.2 then
return "hp_badwnd", healthcolors.badwound
else
return "hp_death", healthcolors.death
end
end
local karmacolors = {
max = Color(255, 255, 255, 255),
high = Color(255, 240, 135, 255),
med = Color(245, 220, 60, 255),
low = Color(255, 180, 0, 255),
min = Color(255, 130, 0, 255),
};
function util.KarmaToString(karma)
local maxkarma = GetGlobalInt("ttt_karma_max", 1000)
if karma > maxkarma * 0.89 then
return "karma_max", karmacolors.max
elseif karma > maxkarma * 0.8 then
return "karma_high", karmacolors.high
elseif karma > maxkarma * 0.65 then
return "karma_med", karmacolors.med
elseif karma > maxkarma * 0.5 then
return "karma_low", karmacolors.low
else
return "karma_min", karmacolors.min
end
end
function util.IncludeClientFile(file)
include(file)
end
else
function util.IncludeClientFile(file)
AddCSLuaFile(file)
end
end
-- Like string.FormatTime but simpler (and working), always a string, no hour
-- support
function util.SimpleTime(seconds, fmt)
if not seconds then seconds = 0 end
local ms = (seconds - math.floor(seconds)) * 100
seconds = math.floor(seconds)
local s = seconds % 60
seconds = (seconds - s) / 60
local m = seconds % 60
return string.format(fmt, m, s, ms)
end