mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
588 lines
12 KiB
Lua
588 lines
12 KiB
Lua
--[[
|
|
| 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/
|
|
--]]
|
|
|
|
local Vector = Vector
|
|
local Matrix = Matrix
|
|
local isstring = isstring
|
|
|
|
do -- table copy
|
|
local lookup_table
|
|
|
|
local function copy(obj, skip_meta)
|
|
local t = type(obj)
|
|
|
|
if t == "number" or t == "string" or t == "function" or t == "boolean" then
|
|
return obj
|
|
end
|
|
|
|
if t == "Vector" or t == "Angle" then
|
|
return obj * 1
|
|
elseif lookup_table[obj] then
|
|
return lookup_table[obj]
|
|
elseif t == "table" then
|
|
local new_table = {}
|
|
|
|
lookup_table[obj] = new_table
|
|
|
|
for key, val in pairs(obj) do
|
|
new_table[copy(key, skip_meta)] = copy(val, skip_meta)
|
|
end
|
|
|
|
return skip_meta and new_table or setmetatable(new_table, getmetatable(obj))
|
|
end
|
|
|
|
return obj
|
|
end
|
|
|
|
function pac.CopyValue(obj, skip_meta)
|
|
lookup_table = {}
|
|
return copy(obj, skip_meta)
|
|
end
|
|
end
|
|
|
|
|
|
function pac.MakeMaterialUnlitGeneric(mat, id)
|
|
local tex_path = mat:GetString("$basetexture")
|
|
|
|
if tex_path then
|
|
local params = {}
|
|
|
|
params["$basetexture"] = tex_path
|
|
params["$vertexcolor"] = 1
|
|
params["$vertexalpha"] = 1
|
|
|
|
return pac.CreateMaterial(pac.uid("pac_fixmat_") .. id, "UnlitGeneric", params)
|
|
end
|
|
|
|
return mat
|
|
end
|
|
|
|
do
|
|
local inf, ninf = math.huge, -math.huge
|
|
|
|
function pac.IsNumberValid(num)
|
|
return
|
|
num and
|
|
num ~= inf and
|
|
num ~= ninf and
|
|
(num >= 0 or num <= 0)
|
|
end
|
|
end
|
|
|
|
do
|
|
pac.next_frame_funcs = pac.next_frame_funcs or {}
|
|
pac.next_frame_funcs_simple = pac.next_frame_funcs_simple or {}
|
|
|
|
function pac.RunNextFrame(id, func)
|
|
pac.next_frame_funcs[id] = func
|
|
end
|
|
|
|
function pac.RunNextFrameSimple(func)
|
|
table.insert(pac.next_frame_funcs_simple, func)
|
|
end
|
|
end
|
|
|
|
do --dev util
|
|
function pac.RemoveAllPACEntities()
|
|
for _, ent in pairs(ents.GetAll()) do
|
|
pac.UnhookEntityRender(ent)
|
|
|
|
if ent.IsPACEntity then
|
|
ent:Remove()
|
|
end
|
|
end
|
|
end
|
|
|
|
local _part
|
|
|
|
local function nuke_part()
|
|
_part:Remove()
|
|
end
|
|
|
|
function pac.Panic()
|
|
pac.RemoveAllParts()
|
|
pac.RemoveAllPACEntities()
|
|
|
|
for i, ent in ipairs(ents.GetAll()) do
|
|
ent.pac_ignored = nil
|
|
ent.pac_ignored_data = nil
|
|
ent.pac_drawing = nil
|
|
ent.pac_shouldnotdraw = nil
|
|
ent.pac_onuse_only = nil
|
|
ent.pac_onuse_only_check = nil
|
|
ent.pac_ignored_callbacks = nil
|
|
|
|
if ent.pac_bones_once then
|
|
pac.ResetBones(ent)
|
|
ent.pac_bones_once = nil
|
|
end
|
|
|
|
if istable(ent.pac_animation_sequences) then
|
|
for part in next, ent.pac_animation_sequences do
|
|
if part:IsValid() then
|
|
_part = part
|
|
ProtectedCall(nuke_part)
|
|
end
|
|
end
|
|
|
|
ent.pac_animation_sequences = nil
|
|
end
|
|
|
|
if istable(ent.pac_bone_parts) then
|
|
for part in next, ent.pac_bone_parts do
|
|
if part:IsValid() then
|
|
_part = part
|
|
ProtectedCall(nuke_part)
|
|
end
|
|
end
|
|
|
|
ent.pac_bone_parts = nil
|
|
end
|
|
|
|
ent.pac_animation_stack = nil
|
|
end
|
|
end
|
|
|
|
pac.convarcache = {}
|
|
function pac.CreateClientConVarFast(cvar,initial,save,t,server)
|
|
|
|
local cached = pac.convarcache[cvar]
|
|
if cached then return cached[1],cached[2] end
|
|
|
|
local val
|
|
local c = CreateClientConVar(cvar,initial,save,server)
|
|
|
|
local ConVarChanged
|
|
|
|
if t == "string" or t == "str" then
|
|
ConVarChanged = function( cvar, old, new )
|
|
val = new
|
|
end
|
|
elseif t == "boolean" or t == "bool" then
|
|
ConVarChanged = function( cvar, old, new )
|
|
if new == "0" then
|
|
val = false
|
|
elseif new == "1" then
|
|
val = true
|
|
else
|
|
val = (tonumber(new) or 0)>=1
|
|
end
|
|
end
|
|
|
|
elseif t == "number" or t == "num" then
|
|
ConVarChanged = function( cvar, old, new )
|
|
val= tonumber( new ) or 0
|
|
end
|
|
|
|
elseif t == "integer" or t == "int" then
|
|
ConVarChanged = function( cvar, old, new )
|
|
val= math.floor(tonumber( new ) or 0)
|
|
end
|
|
end
|
|
|
|
if not ConVarChanged then error("Invalid type: " .. tostring(t)) end
|
|
cvars.AddChangeCallback(cvar, ConVarChanged)
|
|
ConVarChanged(cvar, nil, c:GetString())
|
|
|
|
local function GetConVarValue() return val end
|
|
|
|
pac.convarcache[cvar]={GetConVarValue,c}
|
|
return GetConVarValue,c
|
|
end
|
|
end
|
|
|
|
do
|
|
local hue =
|
|
{
|
|
"red",
|
|
"orange",
|
|
"yellow",
|
|
"green",
|
|
"turquoise",
|
|
"blue",
|
|
"purple",
|
|
"magenta",
|
|
}
|
|
|
|
local sat =
|
|
{
|
|
"pale",
|
|
"",
|
|
"strong",
|
|
}
|
|
|
|
local val =
|
|
{
|
|
"dark",
|
|
"",
|
|
"bright"
|
|
}
|
|
|
|
function pac.HSVToNames(h,s,v)
|
|
return
|
|
hue[math.Round((1+(h/360)*#hue))] or hue[1],
|
|
sat[math.ceil(s*#sat)] or sat[1],
|
|
val[math.ceil(v*#val)] or val[1]
|
|
end
|
|
|
|
function pac.ColorToNames(c)
|
|
if c.r == 255 and c.g == 255 and c.b == 255 then return "white", "", "bright" end
|
|
if c.r == 0 and c.g == 0 and c.b == 0 then return "black", "", "bright" end
|
|
return pac.HSVToNames(ColorToHSV(Color(c.r, c.g, c.b)))
|
|
end
|
|
|
|
|
|
function pac.PrettifyName(str)
|
|
if not str then return end
|
|
str = str:lower()
|
|
str = str:gsub("_", " ")
|
|
return str
|
|
end
|
|
|
|
end
|
|
|
|
do
|
|
local pac_error_mdl = CreateClientConVar("pac_error_mdl","1",true,false,"0 = default error, 1=custom error model, models/yourmodel.mdl")
|
|
local tc
|
|
local invalidCache = {}
|
|
|
|
function pac.FilterInvalidModel(mdl, fallback)
|
|
if not isstring(mdl) then
|
|
mdl = ""
|
|
end
|
|
|
|
if util.IsValidModel(mdl) then
|
|
invalidCache[mdl] = nil
|
|
return mdl
|
|
end
|
|
|
|
mdl = mdl:lower():Trim()
|
|
|
|
if mdl == "" then
|
|
return "models/error.mdl"
|
|
end
|
|
|
|
if invalidCache[mdl] then
|
|
return invalidCache[mdl]
|
|
end
|
|
|
|
-- IsValidModel doesn't always return true... this is expensive though :(
|
|
if string.GetExtensionFromFilename(mdl) == "mdl" and file.Exists(mdl, "GAME") then
|
|
return mdl
|
|
end
|
|
|
|
if fallback and fallback:len() > 0 and (util.IsValidModel(fallback) or file.Exists(fallback , "GAME")) then
|
|
return fallback
|
|
end
|
|
|
|
pac.Message("Invalid model - ", mdl)
|
|
|
|
local str = pac_error_mdl:GetString()
|
|
|
|
if str == "1" or str == "" then
|
|
--passthrough
|
|
elseif str == "0" then
|
|
invalidCache[mdl] = mdl
|
|
return mdl
|
|
elseif util.IsValidModel(str) then
|
|
invalidCache[mdl] = str
|
|
return str
|
|
end
|
|
|
|
if tc == nil then
|
|
if util.IsValidModel("models/props_junk/PopCan01a.mdl") then
|
|
tc = "models/props_junk/PopCan01a.mdl"
|
|
else
|
|
tc = "models/props_junk/popcan01a.mdl"
|
|
end
|
|
end
|
|
|
|
invalidCache[mdl] = tc
|
|
return tc
|
|
end
|
|
end
|
|
|
|
local pac_debug_clmdl = CreateClientConVar("pac_debug_clmdl", "0", true)
|
|
RunConsoleCommand("pac_debug_clmdl", "0")
|
|
|
|
local matsalt = '_' .. (os.time() - 0x40000000)
|
|
|
|
function pac.CreateMaterial(name, ...)
|
|
return CreateMaterial(name .. matsalt, ...)
|
|
end
|
|
|
|
function pac.CreateEntity(model)
|
|
model = pac.FilterInvalidModel(model, fallback)
|
|
|
|
local ent = NULL
|
|
|
|
local type = pac_debug_clmdl:GetInt()
|
|
if type == 0 then
|
|
ent = ClientsideModel(model) or ent
|
|
elseif type == 1 then
|
|
local rag = ClientsideRagdoll(model) or NULL
|
|
if not rag:IsValid() then
|
|
ent = ClientsideModel(model) or ent
|
|
else
|
|
ent = rag
|
|
end
|
|
elseif type == 2 then
|
|
ent = ents.CreateClientProp(model) or ent -- doesn't render properly
|
|
if ent:IsValid() then
|
|
ent:PhysicsDestroy()
|
|
end
|
|
elseif type == 3 then
|
|
|
|
effects.Register(
|
|
{
|
|
Init = function(self, p)
|
|
self:SetModel(model)
|
|
ent = self
|
|
end,
|
|
|
|
Think = function()
|
|
return true
|
|
end,
|
|
|
|
Render = function(self)
|
|
if self.Draw then self:Draw() else self:DrawModel() end
|
|
end
|
|
},
|
|
|
|
"pac_model"
|
|
)
|
|
|
|
util.Effect("pac_model", EffectData())
|
|
end
|
|
|
|
if not ent:IsValid() then
|
|
pac.Message("Failed to create entity with model: ", model)
|
|
end
|
|
|
|
return ent
|
|
end
|
|
|
|
|
|
do -- hook helpers
|
|
pac.added_hooks = pac.added_hooks or {}
|
|
|
|
function pac.AddHook(event_name, id, func, priority)
|
|
id = "pac_" .. id
|
|
|
|
if not DLib and not ULib then
|
|
priority = nil
|
|
end
|
|
|
|
if pac.IsEnabled() then
|
|
hook.Add(event_name, id, func, priority)
|
|
end
|
|
|
|
pac.added_hooks[event_name .. id] = {event_name = event_name, id = id, func = func, priority = priority}
|
|
end
|
|
|
|
function pac.RemoveHook(event_name, id)
|
|
id = "pac_" .. id
|
|
|
|
local data = pac.added_hooks[event_name .. id]
|
|
|
|
if data then
|
|
hook.Remove(data.event_name, data.id)
|
|
|
|
pac.added_hooks[event_name .. id] = nil
|
|
end
|
|
end
|
|
|
|
function pac.CallHook(str, ...)
|
|
return hook.Run("pac_" .. str, ...)
|
|
end
|
|
|
|
function pac.EnableAddedHooks()
|
|
for _, data in pairs(pac.added_hooks) do
|
|
hook.Add(data.event_name, data.id, data.func, data.priority)
|
|
end
|
|
end
|
|
|
|
function pac.DisableAddedHooks()
|
|
for _, data in pairs(pac.added_hooks) do
|
|
hook.Remove(data.event_name, data.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
function pac.Material(str, part)
|
|
if str == "" then return end
|
|
|
|
local ply_owner = part:GetPlayerOwner()
|
|
|
|
return pac.GetPropertyFromName("GetRawMaterial", str, ply_owner) or Material(str)
|
|
end
|
|
|
|
do
|
|
--TODO: Table keeping id -> idx mapping
|
|
local idx = math.random(0x1000)
|
|
function pac.uid(id)
|
|
idx = idx + 1
|
|
if idx>=2^53 then
|
|
ErrorNoHalt("?????BUG???? Pac UIDs exhausted\n")
|
|
idx = 0
|
|
end
|
|
|
|
return ("%s%d"):format(id, idx)
|
|
end
|
|
end
|
|
|
|
function pac.Handleurltex(part, url, callback, shader, additionalData)
|
|
if not url or not pac.urltex or not url:find("http") then return false end
|
|
local skip_cache = url:sub(1,1) == "_"
|
|
|
|
if not url:match("https?://.+/%S*") then return false end
|
|
|
|
pac.urltex.GetMaterialFromURL(
|
|
pac.FixUrl(url),
|
|
|
|
function(mat, tex)
|
|
if not part:IsValid() then return end
|
|
|
|
if callback then
|
|
callback(mat, tex)
|
|
else
|
|
part.Materialm = mat
|
|
part:CallRecursive("OnMaterialChanged")
|
|
end
|
|
|
|
pac.dprint("set custom material texture %q to %s", url, part:GetName())
|
|
end,
|
|
|
|
skip_cache,
|
|
shader,
|
|
nil,
|
|
nil,
|
|
additionalData
|
|
)
|
|
return true
|
|
end
|
|
|
|
local mat
|
|
|
|
for _, ent in pairs(ents.GetAll()) do
|
|
ent.pac_can_legacy_scale = nil
|
|
end
|
|
|
|
function pac.LegacyScale(ent)
|
|
local mat0 = ent:GetBoneMatrix(0)
|
|
if mat0 then
|
|
local mat = Matrix()
|
|
mat:Scale(ent.pac_model_scale)
|
|
ent:SetBoneMatrix(0, mat0 * mat)
|
|
ent.pac_can_legacy_scale = true
|
|
end
|
|
end
|
|
|
|
function pac.SetModelScale(ent, scale, size, legacy_scale)
|
|
if not ent:IsValid() then return end
|
|
|
|
if scale and size then
|
|
ent.pac_model_scale = scale * size
|
|
end
|
|
|
|
if scale and not size then
|
|
ent.pac_model_scale = scale
|
|
end
|
|
|
|
if not scale and size then
|
|
ent.pac_model_scale = Vector(size, size, size)
|
|
end
|
|
|
|
if legacy_scale and (ent.pac_can_legacy_scale == nil or ent.pac_can_legacy_scale == true) then
|
|
ent.pac_matrixhack = true
|
|
|
|
if not ent.pac_follow_bones_function then
|
|
ent.pac_follow_bones_function = pac.build_bone_callback
|
|
ent:AddCallback("BuildBonePositions", function(ent) pac.build_bone_callback(ent) end)
|
|
end
|
|
|
|
ent:DisableMatrix("RenderMultiply")
|
|
else
|
|
ent.pac_matrixhack = false
|
|
|
|
if scale then
|
|
mat = Matrix()
|
|
|
|
local x,y,z = scale.x, scale.y, scale.z
|
|
--local x,y,z = ent.pac_model_scale.x, ent.pac_model_scale.y, ent.pac_model_scale.z
|
|
|
|
mat:Scale(Vector(x,y,z))
|
|
if mat:IsIdentity() then
|
|
ent:DisableMatrix("RenderMultiply")
|
|
else
|
|
ent:EnableMatrix("RenderMultiply", mat)
|
|
end
|
|
end
|
|
|
|
if size then
|
|
if ent.pac_enable_ik then
|
|
ent:SetIK(true)
|
|
ent:SetModelScale(1, 0)
|
|
else
|
|
ent:SetIK(false)
|
|
ent:SetModelScale(size == 1 and 1.000001 or size, 0)
|
|
end
|
|
end
|
|
|
|
if not scale and not size then
|
|
ent:DisableMatrix("RenderMultiply")
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
-- no need to rematch the same pattern
|
|
local pattern_cache = {{}}
|
|
|
|
function pac.StringFind(a, b, simple, case_sensitive)
|
|
if not a or not b then return end
|
|
|
|
if simple and not case_sensitive then
|
|
a = a:lower()
|
|
b = b:lower()
|
|
end
|
|
|
|
pattern_cache[a] = pattern_cache[a] or {}
|
|
|
|
if pattern_cache[a][b] ~= nil then
|
|
return pattern_cache[a][b]
|
|
end
|
|
|
|
if simple and a:find(b, nil, true) or not simple and a:find(b) then
|
|
pattern_cache[a][b] = true
|
|
return true
|
|
else
|
|
pattern_cache[a][b] = false
|
|
return false
|
|
end
|
|
end
|
|
|
|
function pac.TogglePartDrawing(ent, b)
|
|
if b then
|
|
ent.pac_drawing = false
|
|
pac.ShowEntityParts(ent)
|
|
ent.pac_shouldnotdraw = false
|
|
else
|
|
ent.pac_drawing = true
|
|
pac.HideEntityParts(ent)
|
|
ent.pac_shouldnotdraw = true
|
|
end
|
|
end
|
|
|
|
-- disable pop/push flashlight modes (used for stability in 2D context)
|
|
function pac.FlashlightDisable(b)
|
|
pac.flashlight_disabled = b
|
|
end
|