-- 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 pairs(player.GetAll()) 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 pairs(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 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, DoBleed, ent) end local zapsound = Sound("npc/assassin/ball_zap1.wav") function util.EquipmentDestroyed(pos) local effect = EffectData() effect:SetOrigin(pos) util.Effect("cball_explode", effect) WorldSound(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.SafeRemoveHook(event, name) local h = hook.GetTable() if h and h[event] and h[event][name] then hook.Remove(event, name) end end function util.UnbridledHateForULX() util.SafeRemoveHook("PlayerSay", "ULXMeCheck") end function util.noop() end function util.passthrough(x) return x end -- Nice Fisher-Yates implementation, from Wikipedia local rand = math.random function table.Shuffle(t) local n = #t while n > 2 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 type(val) == "table" 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 Warning = ErrorNoHalt function Dev(level, ...) if server_settings and server_settings.Int("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.Classname == "prop_ragdoll" 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) if health > 90 then return "hp_healthy", healthcolors.healthy elseif health > 70 then return "hp_hurt", healthcolors.hurt elseif health > 45 then return "hp_wounded", healthcolors.wounded elseif health > 20 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) if karma > 890 then return "karma_max", karmacolors.max elseif karma > 800 then return "karma_high", karmacolors.high elseif karma > 650 then return "karma_med", karmacolors.med elseif karma > 500 then return "karma_low", karmacolors.low else return "karma_min", karmacolors.min end end end