mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
379
gamemodes/terrortown/gamemode/util.lua
Normal file
379
gamemodes/terrortown/gamemode/util.lua
Normal file
@@ -0,0 +1,379 @@
|
||||
--[[
|
||||
| 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
|
||||
Reference in New Issue
Block a user