mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
Upload
This commit is contained in:
216
lua/pac3/core/client/base_drawable.lua
Normal file
216
lua/pac3/core/client/base_drawable.lua
Normal file
@@ -0,0 +1,216 @@
|
||||
--[[
|
||||
| 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 render_OverrideAlphaWriteEnable = render.OverrideAlphaWriteEnable
|
||||
local render_OverrideColorWriteEnable = render.OverrideColorWriteEnable
|
||||
local render_OverrideBlendFunc = render.OverrideBlendFunc
|
||||
local ProtectedCall = ProtectedCall
|
||||
local cam_IgnoreZ = cam.IgnoreZ
|
||||
local pac = pac
|
||||
local ipairs = ipairs
|
||||
local table = table
|
||||
local TEXFILTER_POINT = TEXFILTER.POINT
|
||||
local render_PopFilterMag = render.PopFilterMag
|
||||
local render_PopFilterMin = render.PopFilterMin
|
||||
local render_PushFilterMin = render.PushFilterMin
|
||||
local render_PushFilterMag = render.PushFilterMag
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.ClassName = "base_drawable"
|
||||
PART.BaseName = PART.ClassName
|
||||
|
||||
BUILDER
|
||||
:StartStorableVars()
|
||||
:SetPropertyGroup("appearance")
|
||||
:GetSet("Translucent", false)
|
||||
:GetSet("IgnoreZ", false)
|
||||
:GetSet("NoTextureFiltering", false)
|
||||
:GetSet("BlendMode", "", {enums = {
|
||||
none = "one;zero;one;zero",
|
||||
alpha = "src_alpha;one_minus_src_alpha;one;one_minus_src_alpha",
|
||||
multiplicative = "dst_color;zero;dst_color;zero",
|
||||
premultiplied = "one;one_src_minus_alpha;one;one_src_minus_alpha",
|
||||
additive = "src_alpha;one;src_alpha;one",
|
||||
}})
|
||||
:EndStorableVars()
|
||||
|
||||
do
|
||||
local blend_modes = {
|
||||
zero = 0,
|
||||
one = 1,
|
||||
dst_color = 2,
|
||||
one_minus_dst_color = 3,
|
||||
src_alpha = 4,
|
||||
one_minus_src_alpha = 5,
|
||||
dst_alpha = 6,
|
||||
one_minus_dst_alpha = 7,
|
||||
src_alpha_saturate = 8,
|
||||
src_color = 9,
|
||||
one_minus_src_color = 10,
|
||||
}
|
||||
|
||||
function PART:SetBlendMode(str)
|
||||
str = str:lower():gsub("%s+", ""):gsub(",", ";"):gsub("blend_", "")
|
||||
|
||||
self.BlendMode = str
|
||||
|
||||
local tbl = str:Split(";")
|
||||
local src_color
|
||||
local dst_color
|
||||
|
||||
local src_alpha
|
||||
local dst_alpha
|
||||
|
||||
if tbl[1] then src_color = blend_modes[tbl[1]] end
|
||||
if tbl[2] then dst_color = blend_modes[tbl[2]] end
|
||||
|
||||
if tbl[3] then src_alpha = blend_modes[tbl[3]] end
|
||||
if tbl[4] then dst_alpha = blend_modes[tbl[4]] end
|
||||
|
||||
if src_color and dst_color then
|
||||
self.blend_override = {src_color, dst_color, src_alpha or 0, dst_alpha or 0, tbl[5]}
|
||||
else
|
||||
self.blend_override = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:StartBlend()
|
||||
if self.blend_override then
|
||||
render_OverrideBlendFunc(true,
|
||||
self.blend_override[1],
|
||||
self.blend_override[2],
|
||||
self.blend_override[3],
|
||||
self.blend_override[4]
|
||||
)
|
||||
|
||||
if self.blend_override[5] then
|
||||
render_OverrideAlphaWriteEnable(true, self.blend_override[5] == "write_alpha")
|
||||
end
|
||||
|
||||
if self.blend_override[6] then
|
||||
render_OverrideColorWriteEnable(true, self.blend_override[6] == "write_color")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:StopBlend()
|
||||
if self.blend_override then
|
||||
render_OverrideBlendFunc(false)
|
||||
|
||||
if self.blend_override[5] then
|
||||
render_OverrideAlphaWriteEnable(false)
|
||||
end
|
||||
|
||||
if self.blend_override[6] then
|
||||
render_OverrideColorWriteEnable(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do -- modifiers
|
||||
PART.HandleModifiersManually = false
|
||||
|
||||
function PART:AddModifier(part)
|
||||
self:RemoveModifier(part)
|
||||
table.insert(self.modifiers, part)
|
||||
end
|
||||
|
||||
function PART:RemoveModifier(part)
|
||||
for i, v in ipairs(self.modifiers) do
|
||||
if v == part then
|
||||
table.remove(self.modifiers, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:ModifiersPreEvent(event)
|
||||
if not self.modifiers[1] then return end
|
||||
|
||||
for _, part in ipairs(self.modifiers) do
|
||||
if not part:IsHidden() then
|
||||
|
||||
if not part.pre_draw_events then part.pre_draw_events = {} end
|
||||
if not part.pre_draw_events[event] then part.pre_draw_events[event] = "Pre" .. event end
|
||||
|
||||
if part[part.pre_draw_events[event]] then
|
||||
part[part.pre_draw_events[event]](part)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:ModifiersPostEvent(event)
|
||||
if not self.modifiers[1] then return end
|
||||
|
||||
for _, part in ipairs(self.modifiers) do
|
||||
if not part:IsHidden() then
|
||||
|
||||
if not part.post_draw_events then part.post_draw_events = {} end
|
||||
if not part.post_draw_events[event] then part.post_draw_events[event] = "Post" .. event end
|
||||
|
||||
if part[part.post_draw_events[event]] then
|
||||
part[part.post_draw_events[event]](part)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PART:IsDrawHidden()
|
||||
return self.draw_hidden
|
||||
end
|
||||
|
||||
local _self
|
||||
|
||||
local function call_draw()
|
||||
_self:OnDraw()
|
||||
end
|
||||
|
||||
function PART:Draw(draw_type)
|
||||
if not self.OnDraw or not self.Enabled or self:IsHiddenCached() then return end
|
||||
|
||||
if
|
||||
draw_type == "viewmodel" or draw_type == "hands" or
|
||||
((self.Translucent == true or self.force_translucent == true) and draw_type == "translucent") or
|
||||
((self.Translucent == false or self.force_translucent == false) and draw_type == "opaque")
|
||||
then
|
||||
if not self.HandleModifiersManually then self:ModifiersPreEvent('OnDraw', draw_type) end
|
||||
|
||||
if self.IgnoreZ then cam_IgnoreZ(true) end
|
||||
|
||||
self:StartBlend()
|
||||
|
||||
if self.NoTextureFiltering then
|
||||
render_PushFilterMin(TEXFILTER_POINT)
|
||||
render_PushFilterMag(TEXFILTER_POINT)
|
||||
end
|
||||
|
||||
_self = self
|
||||
|
||||
ProtectedCall(call_draw)
|
||||
|
||||
if self.NoTextureFiltering then
|
||||
render_PopFilterMin()
|
||||
render_PopFilterMag()
|
||||
end
|
||||
|
||||
self:StopBlend()
|
||||
|
||||
if self.IgnoreZ then cam_IgnoreZ(false) end
|
||||
|
||||
if not self.HandleModifiersManually then self:ModifiersPostEvent('OnDraw', draw_type) end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
213
lua/pac3/core/client/base_movable.lua
Normal file
213
lua/pac3/core/client/base_movable.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
local NULL = NULL
|
||||
local Matrix = Matrix
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "base_movable"
|
||||
PART.BaseName = PART.ClassName
|
||||
|
||||
BUILDER
|
||||
:StartStorableVars()
|
||||
:SetPropertyGroup("orientation")
|
||||
:GetSet("Bone", "head")
|
||||
:GetSet("Position", Vector(0,0,0))
|
||||
:GetSet("Angles", Angle(0,0,0))
|
||||
:GetSet("EyeAngles", false)
|
||||
:GetSet("PositionOffset", Vector(0,0,0))
|
||||
:GetSet("AngleOffset", Angle(0,0,0))
|
||||
:GetSetPart("AimPart")
|
||||
:GetSet("AimPartName", "", {enums = {
|
||||
["local eyes"] = "LOCALEYES",
|
||||
["player eyes"] = "PLAYEREYES",
|
||||
["local eyes yaw"] = "LOCALEYES_YAW",
|
||||
["local eyes pitch"] = "LOCALEYES_PITCH",
|
||||
}})
|
||||
:GetSetPart("Parent")
|
||||
:EndStorableVars()
|
||||
|
||||
do -- bones
|
||||
function PART:SetBone(val)
|
||||
self.Bone = val
|
||||
pac.ResetBoneCache(self:GetOwner())
|
||||
end
|
||||
|
||||
function PART:GetBonePosition()
|
||||
local parent = self:GetParent()
|
||||
if parent:IsValid() then
|
||||
if parent.ClassName == "jiggle" then
|
||||
return parent.pos, parent.ang
|
||||
elseif
|
||||
not parent.is_model_part and
|
||||
not parent.is_entity_part and
|
||||
not parent.is_bone_part and
|
||||
not self.is_bone_part and
|
||||
parent.WorldMatrix
|
||||
then
|
||||
return parent:GetWorldPosition(), parent:GetWorldAngles()
|
||||
end
|
||||
end
|
||||
|
||||
local owner = self:GetParentOwner()
|
||||
if owner:IsValid() then
|
||||
-- if there is no parent, default to owner bones
|
||||
return pac.GetBonePosAng(owner, self.BoneOverride or self.Bone)
|
||||
end
|
||||
|
||||
return Vector(), Angle()
|
||||
end
|
||||
|
||||
function PART:GetBoneMatrix()
|
||||
local parent = self:GetParent()
|
||||
if parent:IsValid() then
|
||||
if parent.ClassName == "jiggle" then
|
||||
local bone_matrix = Matrix()
|
||||
bone_matrix:SetTranslation(parent.pos)
|
||||
bone_matrix:SetAngles(parent.ang)
|
||||
return bone_matrix
|
||||
elseif
|
||||
not parent.is_model_part and
|
||||
not parent.is_entity_part and
|
||||
not parent.is_bone_part and
|
||||
not self.is_bone_part and
|
||||
parent.WorldMatrix
|
||||
then
|
||||
return parent.WorldMatrix
|
||||
end
|
||||
end
|
||||
|
||||
local bone_matrix = Matrix()
|
||||
local owner = self:GetParentOwner()
|
||||
if owner:IsValid() then
|
||||
-- if there is no parent, default to owner bones
|
||||
local pos, ang = pac.GetBonePosAng(owner, self.BoneOverride or self.Bone)
|
||||
bone_matrix:SetTranslation(pos)
|
||||
bone_matrix:SetAngles(ang)
|
||||
end
|
||||
|
||||
return bone_matrix
|
||||
end
|
||||
|
||||
function PART:GetModelBones()
|
||||
return pac.GetModelBones(self:GetOwner())
|
||||
end
|
||||
|
||||
function PART:GetModelBoneIndex()
|
||||
local bones = self:GetModelBones()
|
||||
local owner = self:GetOwner()
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
local name = self.Bone
|
||||
|
||||
if bones[name] and not bones[name].is_special then
|
||||
return owner:LookupBone(bones[name].real)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:BuildWorldMatrix(with_offsets)
|
||||
local local_matrix = Matrix()
|
||||
local_matrix:SetTranslation(self.Position)
|
||||
local_matrix:SetAngles(self.Angles)
|
||||
|
||||
local m = self:GetBoneMatrix() * local_matrix
|
||||
|
||||
m:SetAngles(self:CalcAngles(m:GetAngles(), m:GetTranslation()))
|
||||
|
||||
if with_offsets then
|
||||
m:Translate(self.PositionOffset)
|
||||
m:Rotate(self.AngleOffset)
|
||||
end
|
||||
|
||||
return m
|
||||
end
|
||||
|
||||
function PART:GetWorldMatrixWithoutOffsets()
|
||||
-- this is only used by the editor, no need to cache
|
||||
return self:BuildWorldMatrix(false)
|
||||
end
|
||||
|
||||
function PART:GetWorldMatrix()
|
||||
if not self.WorldMatrix or pac.FrameNumber ~= self.last_framenumber then
|
||||
self.last_framenumber = pac.FrameNumber
|
||||
self.WorldMatrix = self:BuildWorldMatrix(true)
|
||||
end
|
||||
|
||||
return self.WorldMatrix
|
||||
end
|
||||
|
||||
function PART:GetWorldAngles()
|
||||
return self:GetWorldMatrix():GetAngles()
|
||||
end
|
||||
|
||||
function PART:GetWorldPosition()
|
||||
return self:GetWorldMatrix():GetTranslation()
|
||||
end
|
||||
|
||||
function PART:GetDrawPosition()
|
||||
return self:GetWorldPosition(), self:GetWorldAngles()
|
||||
end
|
||||
|
||||
function PART:CalcAngles(ang, wpos)
|
||||
wpos = wpos or self.WorldMatrix and self.WorldMatrix:GetTranslation()
|
||||
if not wpos then return ang end
|
||||
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
|
||||
if pac.StringFind(self.AimPartName, "LOCALEYES_YAW", true, true) then
|
||||
ang = (pac.EyePos - wpos):Angle()
|
||||
ang.p = 0
|
||||
return self.Angles + ang
|
||||
end
|
||||
|
||||
if pac.StringFind(self.AimPartName, "LOCALEYES_PITCH", true, true) then
|
||||
ang = (pac.EyePos - wpos):Angle()
|
||||
ang.y = 0
|
||||
return self.Angles + ang
|
||||
end
|
||||
|
||||
if pac.StringFind(self.AimPartName, "LOCALEYES", true, true) then
|
||||
return self.Angles + (pac.EyePos - wpos):Angle()
|
||||
end
|
||||
|
||||
|
||||
if pac.StringFind(self.AimPartName, "PLAYEREYES", true, true) then
|
||||
local ent = owner.pac_traceres and owner.pac_traceres.Entity or NULL
|
||||
|
||||
if ent:IsValid() then
|
||||
return self.Angles + (ent:EyePos() - wpos):Angle()
|
||||
end
|
||||
|
||||
return self.Angles + (pac.EyePos - wpos):Angle()
|
||||
end
|
||||
|
||||
if self.AimPart:IsValid() and self.AimPart.GetWorldPosition then
|
||||
return self.Angles + (self.AimPart:GetWorldPosition() - wpos):Angle()
|
||||
end
|
||||
|
||||
if self.EyeAngles then
|
||||
if owner:IsPlayer() then
|
||||
return self.Angles + ((owner.pac_hitpos or owner:GetEyeTraceNoCursor().HitPos) - wpos):Angle()
|
||||
elseif owner:IsNPC() then
|
||||
return self.Angles + ((owner:EyePos() + owner:GetForward() * 100) - wpos):Angle()
|
||||
end
|
||||
end
|
||||
|
||||
return ang or Angle(0,0,0)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
1162
lua/pac3/core/client/base_part.lua
Normal file
1162
lua/pac3/core/client/base_part.lua
Normal file
File diff suppressed because it is too large
Load Diff
492
lua/pac3/core/client/bones.lua
Normal file
492
lua/pac3/core/client/bones.lua
Normal file
@@ -0,0 +1,492 @@
|
||||
--[[
|
||||
| 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 pairs = pairs
|
||||
local IsValid = IsValid
|
||||
local unpack = unpack
|
||||
local LocalToWorld = LocalToWorld
|
||||
local math_sin = math.sin
|
||||
local RealTime = RealTime
|
||||
local Entity = Entity
|
||||
local ipairs = ipairs
|
||||
local NULL = NULL
|
||||
local LerpVector = LerpVector
|
||||
local LerpAngle = LerpAngle
|
||||
local Angle = Angle
|
||||
local Vector = Vector
|
||||
local util_QuickTrace = util.QuickTrace
|
||||
local pac = pac
|
||||
local pac_isCameraAllowed = pac.CreateClientConVarFast("pac_enable_camera_as_bone", "1", true, "boolean")
|
||||
|
||||
pac.BoneNameReplacements =
|
||||
{
|
||||
{"Anim_Attachment", "attach"},
|
||||
{"RH", "right hand"},
|
||||
{"LH", "left hand"},
|
||||
{"_L_", " left "},
|
||||
{"_R_", " right "},
|
||||
{"%p", " "},
|
||||
{"ValveBiped", ""},
|
||||
{"Bip01", ""},
|
||||
{"Neck1", "neck"},
|
||||
{"Head1", "head"},
|
||||
{"Toe0", "toe"},
|
||||
{"lowerarm", "lower arm"},
|
||||
{"Bip", ""},
|
||||
{" R", " right"},
|
||||
{" L", " left"},
|
||||
}
|
||||
|
||||
function pac.GetAllBones(ent)
|
||||
ent = ent or NULL
|
||||
|
||||
local tbl = {}
|
||||
|
||||
if ent:IsValid() then
|
||||
ent:InvalidateBoneCache()
|
||||
pac.SetupBones(ent)
|
||||
|
||||
local count = ent:GetBoneCount() or 0
|
||||
|
||||
for bone = 0, count do
|
||||
local name = ent:GetBoneName(bone)
|
||||
local friendly = name
|
||||
|
||||
if name then
|
||||
for _, value in pairs(pac.BoneNameReplacements) do
|
||||
friendly = friendly:gsub(value[1], value[2])
|
||||
end
|
||||
|
||||
friendly = friendly
|
||||
:Trim()
|
||||
:lower()
|
||||
:gsub("(.-)(%d+)", "%1 %2")
|
||||
|
||||
local parent_i = ent:GetBoneParent(bone)
|
||||
if parent_i == -1 then
|
||||
parent_i = nil
|
||||
end
|
||||
|
||||
tbl[friendly] =
|
||||
{
|
||||
friendly = friendly,
|
||||
real = name,
|
||||
bone = bone,
|
||||
i = bone,
|
||||
parent_i = parent_i,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local attachments = ent:GetAttachments()
|
||||
if attachments then
|
||||
for _, data in pairs(attachments) do
|
||||
local parent_i = ent:GetParentAttachment(data.id)
|
||||
if parent_i == -1 then
|
||||
parent_i = nil
|
||||
end
|
||||
local friendly = data.name or "????"
|
||||
|
||||
friendly = friendly
|
||||
:Trim()
|
||||
:lower()
|
||||
:gsub("(.-)(%d+)", "%1 %2")
|
||||
|
||||
if not tbl[friendly] then -- Some of bones CAN be attachments! So we defined them before already.
|
||||
tbl[friendly] =
|
||||
{
|
||||
friendly = friendly,
|
||||
real = data.name or "?????",
|
||||
id = data.id,
|
||||
i = data.id,
|
||||
parent_i = parent_i,
|
||||
is_attachment = true,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tbl.hitpos = {friendly = "hit position", is_special = true}
|
||||
tbl.footstep = {friendly = "footsteps", is_special = true}
|
||||
tbl.skirt = {friendly = "skirt", is_special = true}
|
||||
tbl.skirt2 = {friendly = "skirt2", is_special = true}
|
||||
tbl.hitpos_world_props = {friendly = "hitpos_world_props", is_special = true}
|
||||
tbl.hitpos_world = {friendly = "hitpos_world", is_special = true}
|
||||
tbl.hitpos_world_noang = {friendly = "hitpos_world_noang", is_special = true}
|
||||
tbl.hitpos_ent_ang = {friendly = "hitpos_ent_ang", is_special = true}
|
||||
tbl.hitpos_ent_ang_zero_pitch = {friendly = "hitpos_ent_ang_zero_pitch", is_special = true}
|
||||
tbl.pos_ang = {friendly = "pos_ang", is_special = true}
|
||||
tbl.pos_eyeang = {friendly = "pos_eyeang", is_special = true}
|
||||
tbl.eyepos_eyeang = {friendly = "eyepos_eyeang", is_special = true}
|
||||
tbl.eyepos_ang = {friendly = "eyepos_ang", is_special = true}
|
||||
tbl.pos_noang = {friendly = "pos_noang", is_special = true}
|
||||
tbl.camera = {friendly = "camera", is_special = true}
|
||||
tbl.player_eyes = {friendly = "player_eyes", is_special = true}
|
||||
tbl.physgun_beam_endpos = {friendly = "physgun_beam_endpos", is_special = true}
|
||||
|
||||
ent.pac_bone_count = count + 1
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
function pac.GetModelBones(ent)
|
||||
if not ent or not ent:IsValid() then return {} end
|
||||
|
||||
if not ent.pac_bones or ent:GetModel() ~= ent.pac_last_model then
|
||||
ent:InvalidateBoneCache()
|
||||
pac.SetupBones(ent)
|
||||
|
||||
if ent.pac_holdtypes then
|
||||
ent.pac_holdtypes = {}
|
||||
end
|
||||
|
||||
ent.pac_bones = pac.GetAllBones(ent)
|
||||
ent.pac_last_model = ent:GetModel()
|
||||
end
|
||||
|
||||
return ent.pac_bones
|
||||
end
|
||||
|
||||
function pac.GetAllFlexes(ent)
|
||||
local out = {}
|
||||
if ent.GetFlexNum and ent:GetFlexNum() > 0 then
|
||||
for i = 0, ent:GetFlexNum() - 1 do
|
||||
local name = ent:GetFlexName(i)
|
||||
out[name:lower()] = {i = i, name = name}
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function pac.GetFlexMap(ent)
|
||||
if not ent or not ent:IsValid() then return {} end
|
||||
|
||||
if not ent.pac_flex_map or ent:GetModel() ~= ent.pac_last_model_flex then
|
||||
ent.pac_flex_map = pac.GetAllFlexes(ent)
|
||||
ent.pac_last_model_flex = ent:GetModel()
|
||||
end
|
||||
|
||||
return ent.pac_flex_map
|
||||
end
|
||||
|
||||
function pac.ResetBoneCache(ent)
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
ent.pac_last_model = nil
|
||||
ent.pac_bones = nil
|
||||
ent.pac_cached_child_bones = nil
|
||||
ent.pac_flex_map = nil
|
||||
|
||||
if ent.pac_holdtypes then
|
||||
ent.pac_holdtypes = {}
|
||||
end
|
||||
end
|
||||
|
||||
local UP = Vector(0,0,1):Angle()
|
||||
|
||||
local function GetBonePosition(ent, id)
|
||||
local pos, ang = ent:GetBonePosition(id)
|
||||
|
||||
if not pos then return end
|
||||
|
||||
if ang.p ~= ang.p then ang.p = 0 end
|
||||
if ang.y ~= ang.y then ang.y = 0 end
|
||||
if ang.r ~= ang.r then ang.r = 0 end
|
||||
|
||||
if pos == ent:GetPos() then
|
||||
local mat = ent:GetBoneMatrix(id)
|
||||
if mat then
|
||||
pos = mat:GetTranslation()
|
||||
ang = mat:GetAngles()
|
||||
end
|
||||
end
|
||||
|
||||
if (ent:GetClass() == "viewmodel" or ent == pac.LocalHands) and
|
||||
ent:GetOwner():IsPlayer() and ent:GetOwner():GetActiveWeapon().ViewModelFlip then
|
||||
ang.r = -ang.r
|
||||
end
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
local angle_origin = Angle(0,0,0)
|
||||
|
||||
function pac.GetBonePosAng(ent, id, parent)
|
||||
if not ent:IsValid() then return Vector(), Angle() end
|
||||
|
||||
if id == "physgun_beam_endpos" then
|
||||
if ent.pac_drawphysgun_event then
|
||||
|
||||
local ply, wep, enabled, target, bone, hitpos = unpack(ent.pac_drawphysgun_event)
|
||||
|
||||
local endpos
|
||||
|
||||
if enabled then
|
||||
if target:IsValid() then
|
||||
if bone ~= 0 then
|
||||
local wpos, wang = target:GetBonePosition(target:TranslatePhysBoneToBone(bone))
|
||||
endpos = LocalToWorld(hitpos, Angle(), wpos, wang)
|
||||
else
|
||||
endpos = target:LocalToWorld(hitpos)
|
||||
end
|
||||
else
|
||||
endpos = ply.pac_traceres and ply.pac_traceres.HitPos or util_QuickTrace(ply:EyePos(), ply:EyeAngles():Forward() * 16000, {ply, ply:GetParent()}).HitPos
|
||||
end
|
||||
end
|
||||
|
||||
return endpos, Angle()
|
||||
end
|
||||
elseif id == "camera" then
|
||||
if pac_isCameraAllowed() then
|
||||
return pac.EyePos, pac.EyeAng
|
||||
else
|
||||
return ent:EyePos(), ent:EyeAngles()
|
||||
end
|
||||
elseif id == "player_eyes" then
|
||||
local oldEnt = ent -- Track reference to the original entity in case we aren't allowed to draw here
|
||||
local ent = ent.pac_traceres and ent.pac_traceres.Entity or util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetParent()}).Entity
|
||||
local allowed = pac_isCameraAllowed()
|
||||
|
||||
if ent:IsValid() and (allowed or ent ~= pac.LocalPlayer) then -- Make sure we don't draw on viewer's screen if we aren't allowed to
|
||||
return ent:EyePos(), ent:EyeAngles()
|
||||
end
|
||||
|
||||
if allowed then
|
||||
return pac.EyePos, pac.EyeAng
|
||||
else
|
||||
return oldEnt:EyePos(), oldEnt:EyeAngles()
|
||||
end
|
||||
elseif id == "pos_ang" then
|
||||
return ent:GetPos(), ent:GetAngles()
|
||||
elseif id == "pos_noang" then
|
||||
return ent:GetPos(), angle_origin
|
||||
elseif id == "pos_eyeang" then
|
||||
return ent:GetPos(), ent:EyeAngles()
|
||||
elseif id == "eyepos_eyeang" then
|
||||
return ent:EyePos(), ent:EyeAngles()
|
||||
elseif id == "eyepos_ang" then
|
||||
return ent:EyePos(), ent:GetAngles()
|
||||
elseif id == "hitpos_world_props" then
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
|
||||
return ent:GetClass() == "prop_physics"
|
||||
end)
|
||||
return res.HitPos, res.HitNormal:Angle()
|
||||
elseif id == "hitpos_world" then
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
|
||||
return ent:IsWorld()
|
||||
end)
|
||||
return res.HitPos, res.HitNormal:Angle()
|
||||
elseif id == "hitpos_world_noang" then
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
|
||||
return ent:IsWorld()
|
||||
end)
|
||||
return res.HitPos, angle_origin
|
||||
elseif id == "hitpos" or id == "hit position" then
|
||||
if ent.pac_traceres then
|
||||
return ent.pac_traceres.HitPos, ent.pac_traceres.HitNormal:Angle()
|
||||
else
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
|
||||
|
||||
return res.HitPos, res.HitNormal:Angle()
|
||||
end
|
||||
elseif id == "hitpos_ent_ang" then
|
||||
if ent.pac_traceres then
|
||||
return ent.pac_traceres.HitPos, ent:EyeAngles()
|
||||
else
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
|
||||
|
||||
return res.HitPos, ent:EyeAngles()
|
||||
end
|
||||
elseif id == "hitpos_ent_ang_zero_pitch" then
|
||||
if ent.pac_traceres then
|
||||
local ang = ent:EyeAngles()
|
||||
ang.p = 0
|
||||
return ent.pac_traceres.HitPos, ang
|
||||
else
|
||||
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
|
||||
|
||||
return res.HitPos, ent:EyeAngles()
|
||||
end
|
||||
elseif id == "footstep" then
|
||||
if ent.pac_last_footstep_pos then
|
||||
return ent.pac_last_footstep_pos, UP
|
||||
end
|
||||
elseif id == "skirt" then
|
||||
local apos, aang = pac.GetBonePosAng(ent, "left thigh", parent)
|
||||
local bpos, bang = pac.GetBonePosAng(ent, "right thigh", parent)
|
||||
|
||||
return LerpVector(0.5, apos, bpos), LerpAngle(0.5, aang, bang)
|
||||
elseif id == "skirt2" then
|
||||
local apos, aang = pac.GetBonePosAng(ent, "left calf", parent)
|
||||
local bpos, bang = pac.GetBonePosAng(ent, "right calf", parent)
|
||||
|
||||
return LerpVector(0.5, apos, bpos), LerpAngle(0.5, aang, bang)
|
||||
end
|
||||
|
||||
local pos, ang
|
||||
|
||||
local bones = pac.GetModelBones(ent)
|
||||
local data = bones[id]
|
||||
|
||||
if data and not data.is_special then
|
||||
if data.is_attachment then
|
||||
if parent and data.parent_i then
|
||||
local posang = ent:GetAttachment(data.parent_i)
|
||||
|
||||
if not posang then
|
||||
posang = ent:GetAttachment(data.id)
|
||||
end
|
||||
|
||||
if posang then
|
||||
pos, ang = posang.Pos, posang.Ang
|
||||
end
|
||||
else
|
||||
local posang = ent:GetAttachment(data.id)
|
||||
if posang then
|
||||
pos, ang = posang.Pos, posang.Ang
|
||||
end
|
||||
end
|
||||
else
|
||||
if parent and data.parent_i then
|
||||
pos, ang = GetBonePosition(ent, data.parent_i)
|
||||
|
||||
if not pos or not ang then
|
||||
pos, ang = GetBonePosition(ent, data.bone)
|
||||
end
|
||||
else
|
||||
pos, ang = GetBonePosition(ent, data.bone)
|
||||
end
|
||||
end
|
||||
else
|
||||
local bone_id = id and ent:LookupBone(id) or nil
|
||||
|
||||
if bone_id then
|
||||
pos, ang = GetBonePosition(ent, bone_id)
|
||||
end
|
||||
end
|
||||
|
||||
if not pos then
|
||||
pos = ent:GetPos()
|
||||
end
|
||||
|
||||
if not ang then
|
||||
if ent:IsPlayer() then
|
||||
ang = ent:EyeAngles()
|
||||
ang.p = 0
|
||||
else
|
||||
ang = ent:GetAngles()
|
||||
end
|
||||
end
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
do -- bone manipulation for boneanimlib
|
||||
local SCALE_RESET = Vector(1, 1, 1)
|
||||
local ORIGIN_RESET = Vector(0, 0, 0)
|
||||
local ANGLE_RESET = Angle(0, 0, 0)
|
||||
|
||||
local entmeta = FindMetaTable("Entity")
|
||||
|
||||
local ManipulateBoneScale = entmeta.ManipulateBoneScale
|
||||
local ManipulateBonePosition = entmeta.ManipulateBonePosition
|
||||
local ManipulateBoneAngles = entmeta.ManipulateBoneAngles
|
||||
local ManipulateBoneJiggle = entmeta.ManipulateBoneJiggle
|
||||
|
||||
function pac.ResetBones(ent)
|
||||
ent.pac_bones_once = true
|
||||
|
||||
local pac_boneanim = ent.pac_boneanim
|
||||
local count = (ent:GetBoneCount() or 0) - 1
|
||||
|
||||
if pac_boneanim then
|
||||
for i = 0, count do
|
||||
ManipulateBoneScale(ent, i, SCALE_RESET)
|
||||
ManipulateBonePosition(ent, i, pac_boneanim.positions[i] or ORIGIN_RESET)
|
||||
ManipulateBoneAngles(ent, i, pac_boneanim.angles[i] or ANGLE_RESET)
|
||||
ManipulateBoneJiggle(ent, i, 0)
|
||||
end
|
||||
else
|
||||
for i = 0, count do
|
||||
ManipulateBoneScale(ent, i, SCALE_RESET)
|
||||
ManipulateBonePosition(ent, i, ORIGIN_RESET)
|
||||
ManipulateBoneAngles(ent, i, ANGLE_RESET)
|
||||
ManipulateBoneJiggle(ent, i, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local i = ent.pac_bones_select_target
|
||||
if i and count >= i then
|
||||
ManipulateBoneScale(ent, i, ent:GetManipulateBoneScale(i) * (1 + math_sin(RealTime() * 4) * 0.1))
|
||||
ent.pac_bones_select_target = nil
|
||||
end
|
||||
|
||||
if ent.pac_touching_flexes then
|
||||
local reset_scale = false
|
||||
for id, time in pairs(ent.pac_touching_flexes) do
|
||||
|
||||
if not reset_scale then
|
||||
ent:SetFlexScale(1)
|
||||
reset_scale = true
|
||||
end
|
||||
|
||||
if time < pac.RealTime then
|
||||
ent:SetFlexWeight(id, 0)
|
||||
else
|
||||
if ent:GetFlexWeight(id) == 0 then
|
||||
ent.pac_touching_flexes[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Call("PAC3ResetBones", nil, ent)
|
||||
end
|
||||
|
||||
function pac.SetEntityBoneMatrix(ent, i, matrix)
|
||||
pac.ManipulateBonePosition(ent, i, matrix:GetTranslation())
|
||||
pac.ManipulateBoneAngles(ent, i, matrix:GetAngles())
|
||||
end
|
||||
|
||||
|
||||
function pac.ResetEntityBoneMatrix(ent, i)
|
||||
pac.ManipulateBoneAngles(ent, i, angle_zero)
|
||||
pac.ManipulateBonePosition(ent, i, vector_origin)
|
||||
end
|
||||
|
||||
function pac.ManipulateBonePosition(ply, id, var)
|
||||
ply.pac_boneanim = ply.pac_boneanim or {positions = {}, angles = {}}
|
||||
|
||||
ply.pac_boneanim.positions[id] = var
|
||||
|
||||
if not ply.pac_has_parts then
|
||||
ply:ManipulateBonePosition(id, var)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.ManipulateBoneAngles(ply, id, var)
|
||||
ply.pac_boneanim = ply.pac_boneanim or {positions = {}, angles = {}}
|
||||
|
||||
ply.pac_boneanim.angles[id] = var
|
||||
|
||||
if not ply.pac_has_parts then
|
||||
ply:ManipulateBoneAngles(id, var)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Entity(1):IsPlayer() and not PAC_RESTART then
|
||||
timer.Simple(0, function()
|
||||
for _, v in ipairs(ents.GetAll()) do
|
||||
pac.ResetBoneCache(v)
|
||||
end
|
||||
end)
|
||||
end
|
||||
239
lua/pac3/core/client/class.lua
Normal file
239
lua/pac3/core/client/class.lua
Normal file
@@ -0,0 +1,239 @@
|
||||
--[[
|
||||
| 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 ipairs = ipairs
|
||||
local table_insert = table.insert
|
||||
local isnumber = isnumber
|
||||
local tonumber = tonumber
|
||||
local isstring = isstring
|
||||
local tostring = tostring
|
||||
local table_Merge = table.Merge
|
||||
local istable = istable
|
||||
local pac = pac
|
||||
local setmetatable = setmetatable
|
||||
|
||||
pac.PartTemplates = pac.PartTemplates or {}
|
||||
pac.VariableOrder = {}
|
||||
pac.GroupOrder = pac.GroupOrder or {}
|
||||
|
||||
do
|
||||
local META = {}
|
||||
META.__index = META
|
||||
|
||||
function META:StartStorableVars()
|
||||
|
||||
self.store = true
|
||||
self.group = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:EndStorableVars()
|
||||
|
||||
self.store = false
|
||||
self.group = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:GetPropData(key)
|
||||
return self.PropertyUserdata and self.PropertyUserdata[key] or nil
|
||||
end
|
||||
|
||||
function META:PropData(key)
|
||||
self.PropertyUserdata = self.PropertyUserdata or {}
|
||||
self.PropertyUserdata[key] = self.PropertyUserdata[key] or {}
|
||||
return self.PropertyUserdata[key]
|
||||
end
|
||||
|
||||
function META:StoreProp(key)
|
||||
self.PART.StorableVars = self.PART.StorableVars or {}
|
||||
self.PART.StorableVars[key] = key
|
||||
end
|
||||
|
||||
function META:RemoveProp(key)
|
||||
self.PropertyUserdata = self.PropertyUserdata or {}
|
||||
self.PropertyUserdata[key] = nil
|
||||
|
||||
self.PART.StorableVars = self.PART.StorableVars or {}
|
||||
self.PART.StorableVars[key] = nil
|
||||
end
|
||||
|
||||
local function insert_key(tbl, key)
|
||||
for _, k in ipairs(tbl) do
|
||||
if k == key then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
table_insert(tbl, key)
|
||||
end
|
||||
|
||||
function META:SetPropertyGroup(name)
|
||||
|
||||
local tbl = self.PART
|
||||
|
||||
self.group = name
|
||||
|
||||
if tbl then
|
||||
pac.GroupOrder[tbl.ClassName] = pac.GroupOrder[tbl.ClassName] or {}
|
||||
insert_key(pac.GroupOrder[tbl.ClassName], name)
|
||||
end
|
||||
|
||||
pac.GroupOrder.none = pac.GroupOrder.none or {}
|
||||
insert_key(pac.GroupOrder.none, name)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:PropertyOrder(key)
|
||||
local tbl = self.PART
|
||||
|
||||
pac.VariableOrder[tbl.ClassName] = pac.VariableOrder[tbl.ClassName] or {}
|
||||
insert_key(pac.VariableOrder[tbl.ClassName], key)
|
||||
|
||||
if self.group then
|
||||
self:PropData(key).group = self.group
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:GetSet(key, def, udata)
|
||||
local tbl = self.PART
|
||||
|
||||
pac.VariableOrder[tbl.ClassName] = pac.VariableOrder[tbl.ClassName] or {}
|
||||
insert_key(pac.VariableOrder[tbl.ClassName], key)
|
||||
|
||||
if isnumber(def) then
|
||||
tbl["Set" .. key] = tbl["Set" .. key] or function(self, var) self[key] = tonumber(var) end
|
||||
tbl["Get" .. key] = tbl["Get" .. key] or function(self) return tonumber(self[key]) end
|
||||
elseif isstring(def) then
|
||||
tbl["Set" .. key] = tbl["Set" .. key] or function(self, var) self[key] = tostring(var) end
|
||||
tbl["Get" .. key] = tbl["Get" .. key] or function(self) return tostring(self[key]) end
|
||||
else
|
||||
tbl["Set" .. key] = tbl["Set" .. key] or function(self, var) self[key] = var end
|
||||
tbl["Get" .. key] = tbl["Get" .. key] or function(self) return self[key] end
|
||||
end
|
||||
|
||||
tbl[key] = def
|
||||
|
||||
if udata then
|
||||
table_Merge(self:PropData(key), udata)
|
||||
end
|
||||
|
||||
if self.store then
|
||||
self:StoreProp(key)
|
||||
end
|
||||
|
||||
if self.group then
|
||||
self:PropData(key).group = self.group
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:GetSetPart(key, udata)
|
||||
udata = udata or {}
|
||||
udata.editor_panel = udata.editor_panel or "part"
|
||||
udata.part_key = key
|
||||
|
||||
local PART = self.PART
|
||||
|
||||
self:GetSet(key .. "UID", "", udata)
|
||||
|
||||
PART["Set" .. key .. "UID"] = function(self, uid)
|
||||
if uid == "" or not uid then
|
||||
self["Set" .. key](self, NULL)
|
||||
self[key.."UID"] = ""
|
||||
return
|
||||
end
|
||||
|
||||
if istable(uid) then
|
||||
uid = uid.UniqueID
|
||||
end
|
||||
|
||||
self[key.."UID"] = uid
|
||||
|
||||
local owner_id = self:GetPlayerOwnerId()
|
||||
local part = pac.GetPartFromUniqueID(owner_id, uid)
|
||||
|
||||
if part:IsValid() then
|
||||
if part == self then
|
||||
part = NULL
|
||||
self[key.."UID"] = ""
|
||||
end
|
||||
|
||||
self["Set" .. key](self, part)
|
||||
elseif uid ~= "" then
|
||||
self.unresolved_uid_parts = self.unresolved_uid_parts or {}
|
||||
self.unresolved_uid_parts[owner_id] = self.unresolved_uid_parts[owner_id] or {}
|
||||
self.unresolved_uid_parts[owner_id][uid] = self.unresolved_uid_parts[owner_id][uid] or {}
|
||||
self.unresolved_uid_parts[owner_id][uid][key] = key
|
||||
end
|
||||
end
|
||||
|
||||
PART["Set" .. key] = PART["Set" .. key] or function(self, var)
|
||||
self[key] = var
|
||||
if var and var:IsValid() then
|
||||
self[key.."UID"] = var:GetUniqueID()
|
||||
else
|
||||
self[key.."UID"] = ""
|
||||
end
|
||||
end
|
||||
PART["Get" .. key] = PART["Get" .. key] or function(self) return self[key] end
|
||||
PART[key] = NULL
|
||||
|
||||
PART.PartReferenceKeys = PART.PartReferenceKeys or {}
|
||||
PART.PartReferenceKeys[key] = key
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:RemoveProperty(key)
|
||||
self.PART["Set" .. key] = nil
|
||||
self.PART["Get" .. key] = nil
|
||||
self.PART["Is" .. key] = nil
|
||||
self.PART[key] = nil
|
||||
|
||||
self:RemoveProp(key)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function META:Register()
|
||||
pac.PartTemplates[self.PART.ClassName] = self
|
||||
|
||||
pac.RegisterPart(self.PART)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function pac.PartTemplate(name)
|
||||
local builder
|
||||
|
||||
if name and pac.PartTemplates[name] then
|
||||
builder = pac.CopyValue(pac.PartTemplates[name])
|
||||
else
|
||||
builder = {PART = {}}
|
||||
builder.PART.Builder = builder
|
||||
end
|
||||
|
||||
return setmetatable(builder, META), builder.PART
|
||||
end
|
||||
|
||||
function pac.GetTemplate(name)
|
||||
return pac.PartTemplates[name]
|
||||
end
|
||||
end
|
||||
|
||||
function pac.GetPropertyUserdata(obj, key)
|
||||
return pac.PartTemplates[obj.ClassName] and pac.PartTemplates[obj.ClassName]:GetPropData(key) or {}
|
||||
end
|
||||
20
lua/pac3/core/client/ear_grab_animation.lua
Normal file
20
lua/pac3/core/client/ear_grab_animation.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- see https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/base/gamemode/animations.lua#L235
|
||||
hook.Add("PostGamemodeLoaded", "pac_ear_grab_animation",function()
|
||||
if GAMEMODE.GrabEarAnimation then -- only add it if it exists
|
||||
local original_ear_grab_animation = GAMEMODE.GrabEarAnimation
|
||||
GAMEMODE.GrabEarAnimation = function(_, ply)
|
||||
if ( ply.pac_disable_ear_grab ) then return end
|
||||
return original_ear_grab_animation(_, ply)
|
||||
end
|
||||
end
|
||||
end)
|
||||
212
lua/pac3/core/client/hooks.lua
Normal file
212
lua/pac3/core/client/hooks.lua
Normal file
@@ -0,0 +1,212 @@
|
||||
--[[
|
||||
| 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 pairs = pairs
|
||||
local IsEntity = IsEntity
|
||||
local next = next
|
||||
|
||||
pac.AddHook("RenderScene", "eyeangles_eyepos", function(pos, ang)
|
||||
pac.EyePos = pos
|
||||
pac.EyeAng = ang
|
||||
end)
|
||||
|
||||
pac.AddHook("DrawPhysgunBeam", "physgun_event", function(ply, wep, enabled, target, bone, hitpos)
|
||||
|
||||
if enabled then
|
||||
ply.pac_drawphysgun_event = {ply, wep, enabled, target, bone, hitpos}
|
||||
else
|
||||
ply.pac_drawphysgun_event = nil
|
||||
end
|
||||
|
||||
local pac_drawphysgun_event_part = ply.pac_drawphysgun_event_part
|
||||
if pac_drawphysgun_event_part then
|
||||
for event in pairs(pac_drawphysgun_event_part) do
|
||||
if event:IsValid() then
|
||||
event:OnThink()
|
||||
else
|
||||
pac_drawphysgun_event_part[event] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ply.pac_hide_physgun_beam then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
do
|
||||
pac.AddHook("UpdateAnimation", "event_part", function(ply)
|
||||
if not IsEntity(ply) or not ply:IsValid() then return end
|
||||
|
||||
if ply.pac_death_physics_parts and ply:Alive() and ply.pac_physics_died then
|
||||
pac.CallRecursiveOnOwnedParts(ply, "OnBecomePhysics")
|
||||
ply.pac_physics_died = false
|
||||
end
|
||||
|
||||
do
|
||||
local tbl = ply.pac_pose_params
|
||||
|
||||
if tbl then
|
||||
for _, data in pairs(tbl) do
|
||||
ply:SetPoseParameter(data.key, data.val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local animrate = 1
|
||||
|
||||
if ply.pac_global_animation_rate and ply.pac_global_animation_rate ~= 1 then
|
||||
if ply.pac_global_animation_rate == 0 then
|
||||
animrate = ply:GetModelScale() * 2
|
||||
elseif ply.pac_global_animation_rate ~= 1 then
|
||||
ply:SetCycle((pac.RealTime * ply.pac_global_animation_rate)%1)
|
||||
animrate = ply.pac_global_animation_rate
|
||||
end
|
||||
end
|
||||
|
||||
if ply.pac_animation_sequences then
|
||||
local part, thing = next(ply.pac_animation_sequences)
|
||||
|
||||
if part and part:IsValid() then
|
||||
|
||||
if part.OwnerCycle then
|
||||
if part.Rate == 0 then
|
||||
animrate = 1
|
||||
ply:SetCycle(part.Offset % 1)
|
||||
else
|
||||
animrate = animrate * part.Rate
|
||||
end
|
||||
end
|
||||
elseif part and not part:IsValid() then
|
||||
ply.pac_animation_sequences[part] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if animrate ~= 1 then
|
||||
ply:SetCycle((pac.RealTime * animrate) % 1)
|
||||
return true
|
||||
end
|
||||
|
||||
if ply.pac_holdtype_alternative_animation_rate then
|
||||
local length = ply:GetVelocity():Dot(ply:EyeAngles():Forward()) > 0 and 1 or -1
|
||||
local scale = ply:GetModelScale() * 2
|
||||
|
||||
if scale ~= 0 then
|
||||
ply:SetCycle(pac.RealTime / scale * length)
|
||||
else
|
||||
ply:SetCycle(0)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local vehicle = ply:GetVehicle()
|
||||
|
||||
if ply.pac_last_vehicle ~= vehicle then
|
||||
if ply.pac_last_vehicle ~= nil then
|
||||
pac.CallRecursiveOnAllParts("OnVehicleChanged", ply, vehicle)
|
||||
end
|
||||
ply.pac_last_vehicle = vehicle
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local MOVETYPE_NOCLIP = MOVETYPE_NOCLIP
|
||||
|
||||
pac.AddHook("TranslateActivity", "events", function(ply, act)
|
||||
if IsEntity(ply) and ply:IsValid() then
|
||||
|
||||
-- animation part
|
||||
if ply.pac_animation_sequences and next(ply.pac_animation_sequences) then
|
||||
-- dont do any holdtype stuff if theres a sequence
|
||||
return
|
||||
end
|
||||
|
||||
if ply.pac_animation_holdtypes then
|
||||
local key, val = next(ply.pac_animation_holdtypes)
|
||||
if key then
|
||||
if not val.part:IsValid() then
|
||||
ply.pac_animation_holdtypes[key] = nil
|
||||
else
|
||||
return val[act]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- holdtype part
|
||||
if ply.pac_holdtypes then
|
||||
local key, act_table = next(ply.pac_holdtypes)
|
||||
|
||||
if key then
|
||||
if not act_table.part:IsValid() then
|
||||
ply.pac_holdtypes[key] = nil
|
||||
else
|
||||
|
||||
if act_table[act] and act_table[act] ~= -1 then
|
||||
return act_table[act]
|
||||
end
|
||||
|
||||
if ply:GetVehicle():IsValid() and ply:GetVehicle():GetClass() == "prop_vehicle_prisoner_pod" then
|
||||
return act_table.sitting
|
||||
end
|
||||
|
||||
if act_table.noclip ~= -1 and ply:GetMoveType() == MOVETYPE_NOCLIP then
|
||||
return act_table.noclip
|
||||
end
|
||||
|
||||
if act_table.air ~= -1 and ply:GetMoveType() ~= MOVETYPE_NOCLIP and not ply:IsOnGround() then
|
||||
return act_table.air
|
||||
end
|
||||
|
||||
if act_table.fallback ~= -1 then
|
||||
return act_table.fallback
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("CalcMainActivity", "events", function(ply, act)
|
||||
if IsEntity(ply) and ply:IsValid() and ply.pac_animation_sequences then
|
||||
local key, val = next(ply.pac_animation_sequences)
|
||||
|
||||
if not key then return end
|
||||
|
||||
if not val.part:IsValid() then
|
||||
ply.pac_animation_sequences[key] = nil
|
||||
return
|
||||
end
|
||||
|
||||
return val.seq, val.seq
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("pac_PlayerFootstep", "events", function(ply, pos, snd, vol)
|
||||
ply.pac_last_footstep_pos = pos
|
||||
|
||||
if ply.pac_footstep_override then
|
||||
for _, part in pairs(ply.pac_footstep_override) do
|
||||
if not part:IsHidden() then
|
||||
part:PlaySound(snd, vol)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ply.pac_mute_footsteps then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("pac_effect_precached", function()
|
||||
local name = net.ReadString()
|
||||
pac.CallHook("EffectPrecached", name)
|
||||
end)
|
||||
84
lua/pac3/core/client/init.lua
Normal file
84
lua/pac3/core/client/init.lua
Normal file
@@ -0,0 +1,84 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pac = pac or {}
|
||||
|
||||
pac.LocalPlayer = LocalPlayer()
|
||||
pac.LocalViewModel = pac.LocalPlayer:IsValid() and pac.LocalPlayer:GetViewModel() or NULL
|
||||
pac.LocalHands = pac.LocalPlayer:IsValid() and pac.LocalPlayer:GetHands() or NULL
|
||||
do
|
||||
local pac_enable = CreateClientConVar("pac_enable", "1", true)
|
||||
|
||||
cvars.AddChangeCallback("pac_enable", function(_, old, new)
|
||||
if old == new then return end
|
||||
|
||||
if tobool(new) then
|
||||
pac.Enable()
|
||||
else
|
||||
pac.Disable()
|
||||
end
|
||||
end)
|
||||
|
||||
function pac.IsEnabled()
|
||||
return pac_enable:GetBool()
|
||||
end
|
||||
|
||||
function pac.Enable()
|
||||
pac.EnableDrawnEntities(true)
|
||||
pac.EnableAddedHooks()
|
||||
pac.CallHook("Enable")
|
||||
pac_enable:SetBool(true)
|
||||
end
|
||||
|
||||
function pac.Disable()
|
||||
pac.EnableDrawnEntities(false)
|
||||
pac.DisableAddedHooks()
|
||||
pac.CallHook("Disable")
|
||||
pac_enable:SetBool(false)
|
||||
end
|
||||
end
|
||||
|
||||
include("util.lua")
|
||||
include("class.lua")
|
||||
|
||||
pac.CompileExpression = include("pac3/libraries/expression.lua")
|
||||
pac.resource = include("pac3/libraries/resource.lua")
|
||||
pac.animations = include("pac3/libraries/animations.lua")
|
||||
|
||||
include("pac3/core/shared/init.lua")
|
||||
|
||||
pac.urltex = include("pac3/libraries/urltex.lua")
|
||||
|
||||
include("parts.lua")
|
||||
include("max_render_time.lua")
|
||||
include("part_pool.lua")
|
||||
include("bones.lua")
|
||||
include("hooks.lua")
|
||||
include("owner_name.lua")
|
||||
include("integration_tools.lua")
|
||||
include("test.lua")
|
||||
include("ear_grab_animation.lua")
|
||||
|
||||
pac.LoadParts()
|
||||
|
||||
hook.Add("OnEntityCreated", "pac_init", function(ent)
|
||||
local ply = LocalPlayer()
|
||||
if not ply:IsValid() then return end
|
||||
|
||||
pac.LocalPlayer = ply
|
||||
pac.LocalViewModel = pac.LocalPlayer:GetViewModel()
|
||||
pac.LocalHands = pac.LocalPlayer:GetHands()
|
||||
|
||||
pac.in_initialize = true
|
||||
hook.Run("pac_Initialized")
|
||||
pac.in_initialize = nil
|
||||
|
||||
hook.Remove("OnEntityCreated", "pac_init")
|
||||
end)
|
||||
349
lua/pac3/core/client/integration_tools.lua
Normal file
349
lua/pac3/core/client/integration_tools.lua
Normal file
@@ -0,0 +1,349 @@
|
||||
--[[
|
||||
| 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 Angle = Angle
|
||||
local RealTime = RealTime
|
||||
local NULL = NULL
|
||||
|
||||
function pac.GenerateNewUniqueID(part_data, base)
|
||||
local part_data = table.Copy(part_data)
|
||||
base = base or tostring(part_data)
|
||||
|
||||
local function fixpart(part)
|
||||
for key, val in pairs(part.self) do
|
||||
if val ~= "" and (key == "UniqueID" or key:sub(-3) == "UID") then
|
||||
part.self[key] = pac.Hash(base .. val)
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in pairs(part.children) do
|
||||
fixpart(part)
|
||||
end
|
||||
end
|
||||
|
||||
return part_data
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local force_draw_localplayer = false
|
||||
|
||||
hook.Add("ShouldDrawLocalPlayer", "pac_draw_2d_entity", function()
|
||||
if force_draw_localplayer == true then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
function pac.DrawEntity2D(ent, x, y, w, h, cam_pos, cam_ang, cam_fov, cam_nearz, cam_farz)
|
||||
|
||||
pac.ShowEntityParts(ent)
|
||||
pac.ForceRendering(true)
|
||||
|
||||
ent = ent or pac.LocalPlayer
|
||||
x = x or 0
|
||||
y = y or 0
|
||||
w = w or 64
|
||||
h = h or 64
|
||||
cam_ang = cam_ang or Angle(0, RealTime() * 25, 0)
|
||||
cam_pos = cam_pos or ent:LocalToWorld(ent:OBBCenter()) - cam_ang:Forward() * ent:BoundingRadius() * 2
|
||||
cam_fov = cam_fov or 90
|
||||
|
||||
pac.SetupBones(ent)
|
||||
|
||||
cam.Start2D()
|
||||
cam.Start3D(cam_pos, cam_ang, cam_fov, x, y, w, h, cam_nearz or 5, cam_farz or 4096)
|
||||
pac.FlashlightDisable(true)
|
||||
|
||||
force_draw_localplayer = true
|
||||
ent:DrawModel()
|
||||
pac.RenderOverride(ent, "opaque")
|
||||
pac.RenderOverride(ent, "translucent")
|
||||
force_draw_localplayer = false
|
||||
|
||||
pac.FlashlightDisable(false)
|
||||
cam.End3D()
|
||||
cam.End2D()
|
||||
|
||||
pac.ForceRendering(false)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.SetupENT(ENT, owner)
|
||||
ENT.pac_owner = ENT.pac_owner or owner or "self"
|
||||
|
||||
local function find(parent, name)
|
||||
for _, part in ipairs(parent:GetChildren()) do
|
||||
|
||||
if part:GetName():lower():find(name) then
|
||||
return part
|
||||
end
|
||||
|
||||
local part = find(part, name)
|
||||
if part then return part end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:FindPACPart(outfit, name)
|
||||
|
||||
name = name:lower()
|
||||
|
||||
if not outfit.self then
|
||||
for _, val in pairs(outfit) do
|
||||
local part = self:FindPACPart(val, name)
|
||||
if part:IsValid() then
|
||||
return part
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
|
||||
self.pac_part_find_cache = self.pac_part_find_cache or {}
|
||||
|
||||
local part = self.pac_outfits[outfit.self.UniqueID] or NULL
|
||||
|
||||
if part:IsValid() then
|
||||
local cached = self.pac_part_find_cache[name] or NULL
|
||||
|
||||
if cached:IsValid() then return cached end
|
||||
|
||||
part = find(part, name)
|
||||
|
||||
|
||||
if part then
|
||||
self.pac_part_find_cache[name] = part
|
||||
|
||||
return part
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
|
||||
function ENT:AttachPACPart(outfit, owner, keep_uniqueid)
|
||||
|
||||
if not outfit.self then
|
||||
return self:AttachPACSession(outfit, owner)
|
||||
end
|
||||
|
||||
if
|
||||
(outfit.self.OwnerName == "viewmodel" or outfit.self.OwnerName == "hands") and
|
||||
self:IsWeapon() and
|
||||
self.Owner:IsValid() and
|
||||
self.Owner:IsPlayer() and
|
||||
self.Owner ~= pac.LocalPlayer
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
if not keep_uniqueid then
|
||||
outfit = pac.GenerateNewUniqueID(outfit, self:EntIndex())
|
||||
end
|
||||
|
||||
owner = owner or self.pac_owner or self.Owner
|
||||
|
||||
if self.pac_owner == "self" then
|
||||
owner = self
|
||||
elseif self[self.pac_owner] then
|
||||
owner = self[self.pac_owner]
|
||||
end
|
||||
|
||||
self.pac_outfits = self.pac_outfits or {}
|
||||
|
||||
local part = self.pac_outfits[outfit.self.UniqueID] or NULL
|
||||
|
||||
if part:IsValid() then
|
||||
part:Remove()
|
||||
end
|
||||
|
||||
part = pac.CreatePart(outfit.self.ClassName, owner, outfit)
|
||||
|
||||
self.pac_outfits[outfit.self.UniqueID] = part
|
||||
|
||||
self.pac_part_find_cache = {}
|
||||
|
||||
if self.pac_show_in_editor == nil then
|
||||
self:SetShowPACPartsInEditor(false)
|
||||
self.pac_show_in_editor = nil
|
||||
end
|
||||
|
||||
return part
|
||||
end
|
||||
|
||||
function ENT:RemovePACPart(outfit, keep_uniqueid)
|
||||
if not outfit.self then
|
||||
return self:RemovePACSession(outfit)
|
||||
end
|
||||
|
||||
if not keep_uniqueid then
|
||||
outfit = pac.GenerateNewUniqueID(outfit, self:EntIndex())
|
||||
end
|
||||
|
||||
self.pac_outfits = self.pac_outfits or {}
|
||||
|
||||
local part = self.pac_outfits[outfit.self.UniqueID] or NULL
|
||||
|
||||
if part:IsValid() then
|
||||
part:Remove()
|
||||
end
|
||||
|
||||
self.pac_part_find_cache = {}
|
||||
end
|
||||
|
||||
function ENT:GetPACPartPosAng(outfit, name)
|
||||
local part = self:FindPACPart(outfit, name)
|
||||
|
||||
if part:IsValid() and part.GetWorldPosition then
|
||||
return part:GetWorldPosition(), part:GetWorldAngles()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AttachPACSession(session)
|
||||
for _, part in pairs(session) do
|
||||
self:AttachPACPart(part)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:RemovePACSession(session)
|
||||
for _, part in pairs(session) do
|
||||
self:RemovePACPart(part)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetPACDrawDistance(dist)
|
||||
self.pac_draw_distance = dist
|
||||
end
|
||||
|
||||
function ENT:GetPACDrawDistance()
|
||||
return self.pac_draw_distance
|
||||
end
|
||||
|
||||
function ENT:SetShowPACPartsInEditor(b)
|
||||
self.pac_outfits = self.pac_outfits or {}
|
||||
|
||||
for _, part in pairs(self.pac_outfits) do
|
||||
part:SetShowInEditor(b)
|
||||
end
|
||||
|
||||
self.pac_show_in_editor = b
|
||||
end
|
||||
|
||||
function ENT:GetShowPACPartsInEditor()
|
||||
return self.pac_show_in_editor
|
||||
end
|
||||
end
|
||||
|
||||
function pac.SetupSWEP(SWEP, owner)
|
||||
SWEP.pac_owner = owner or "Owner"
|
||||
pac.SetupENT(SWEP, owner)
|
||||
end
|
||||
|
||||
function pac.AddEntityClassListener(class, session, check_func, draw_dist)
|
||||
|
||||
if session.self then
|
||||
session = {session}
|
||||
end
|
||||
|
||||
draw_dist = 0
|
||||
check_func = check_func or function(ent) return ent:GetClass() == class end
|
||||
|
||||
local id = "pac_auto_attach_" .. class
|
||||
|
||||
local weapons = {}
|
||||
local function weapon_think()
|
||||
for _, ent in pairs(weapons) do
|
||||
if ent:IsValid() then
|
||||
if ent.Owner and ent.Owner:IsValid() then
|
||||
if not ent.AttachPACSession then
|
||||
pac.SetupSWEP(ent)
|
||||
end
|
||||
|
||||
if ent.Owner:GetActiveWeapon() == ent then
|
||||
if not ent.pac_deployed then
|
||||
ent:AttachPACSession(session)
|
||||
ent.pac_deployed = true
|
||||
end
|
||||
|
||||
ent.pac_last_owner = ent.Owner
|
||||
else
|
||||
if ent.pac_deployed then
|
||||
ent:RemovePACSession(session)
|
||||
ent.pac_deployed = false
|
||||
end
|
||||
end
|
||||
elseif (ent.pac_last_owner or NULL):IsValid() and not ent.pac_last_owner:Alive() then
|
||||
if ent.pac_deployed then
|
||||
ent:RemovePACSession(session)
|
||||
ent.pac_deployed = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function created(ent)
|
||||
if ent:IsValid() and check_func(ent) then
|
||||
if ent:IsWeapon() then
|
||||
weapons[ent:EntIndex()] = ent
|
||||
hook.Add("Think", id, weapon_think)
|
||||
else
|
||||
pac.SetupENT(ent)
|
||||
ent:AttachPACSession(session)
|
||||
ent:SetPACDrawDistance(draw_dist)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function removed(ent)
|
||||
if ent:IsValid() and check_func(ent) and ent.pac_outfits then
|
||||
ent:RemovePACSession(session)
|
||||
weapons[ent:EntIndex()] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for _, ent in pairs(ents.GetAll()) do
|
||||
created(ent)
|
||||
end
|
||||
|
||||
hook.Add("EntityRemoved", id, removed)
|
||||
hook.Add("OnEntityCreated", id, created)
|
||||
end
|
||||
|
||||
function pac.RemoveEntityClassListener(class, session, check_func)
|
||||
if session.self then
|
||||
session = {session}
|
||||
end
|
||||
|
||||
check_func = check_func or function(ent) return ent:GetClass() == class end
|
||||
|
||||
for _, ent in pairs(ents.GetAll()) do
|
||||
if check_func(ent) and ent.pac_outfits then
|
||||
ent:RemovePACSession(session)
|
||||
end
|
||||
end
|
||||
|
||||
local id = "pac_auto_attach_" .. class
|
||||
|
||||
hook.Remove("Think", id)
|
||||
hook.Remove("EntityRemoved", id)
|
||||
hook.Remove("OnEntityCreated", id)
|
||||
end
|
||||
|
||||
timer.Simple(0, function()
|
||||
if easylua and luadev then
|
||||
function easylua.PACSetClassListener(class_name, name, b)
|
||||
if b == nil then
|
||||
b = true
|
||||
end
|
||||
|
||||
luadev.RunOnClients(("pac.%s(%q, {%s})"):format(b and "AddEntityClassListener" or "RemoveEntityClassListener", class_name, file.Read("pac3/".. name .. ".txt", "DATA")))
|
||||
end
|
||||
end
|
||||
end)
|
||||
112
lua/pac3/core/client/max_render_time.lua
Normal file
112
lua/pac3/core/client/max_render_time.lua
Normal file
@@ -0,0 +1,112 @@
|
||||
--[[
|
||||
| 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 SysTime = SysTime
|
||||
local pairs = pairs
|
||||
local Color = Color
|
||||
local tostring = tostring
|
||||
local cam_Start2D = cam.Start2D
|
||||
local cam_IgnoreZ = cam.IgnoreZ
|
||||
local Vector = Vector
|
||||
local math_Clamp = math.Clamp
|
||||
local EyePos = EyePos
|
||||
local surface_SetFont = surface.SetFont
|
||||
local surface_GetTextSize = surface.GetTextSize
|
||||
local draw_DrawText = draw.DrawText
|
||||
local string_format = string.format
|
||||
local input_GetCursorPos = input.GetCursorPos
|
||||
local vgui_CursorVisible = vgui.CursorVisible
|
||||
local ScrW = ScrW
|
||||
local ScrH = ScrH
|
||||
local input_LookupBinding = input.LookupBinding
|
||||
local LocalPlayer = LocalPlayer
|
||||
local input_IsMouseDown = input.IsMouseDown
|
||||
local cam_End2D = cam.End2D
|
||||
|
||||
local max_render_time_cvar = CreateClientConVar("pac_max_render_time", 0)
|
||||
|
||||
function pac.IsRenderTimeExceeded(ent)
|
||||
return ent.pac_render_time_exceeded
|
||||
end
|
||||
|
||||
function pac.ResetRenderTime(ent)
|
||||
ent.pac_rendertime = ent.pac_rendertime or {}
|
||||
|
||||
for key in pairs(ent.pac_rendertime) do
|
||||
ent.pac_rendertime[key] = 0
|
||||
end
|
||||
end
|
||||
|
||||
function pac.RecordRenderTime(ent, type, start)
|
||||
ent.pac_rendertime = ent.pac_rendertime or {}
|
||||
ent.pac_rendertime[type] = (ent.pac_rendertime[type] or 0) + (SysTime() - start)
|
||||
|
||||
local max_render_time = max_render_time_cvar:GetFloat()
|
||||
|
||||
if max_render_time > 0 then
|
||||
local total_time = 0
|
||||
|
||||
for k,v in pairs(ent.pac_rendertime) do
|
||||
total_time = total_time + v
|
||||
end
|
||||
|
||||
total_time = total_time * 1000
|
||||
|
||||
if total_time > max_render_time then
|
||||
pac.Message(Color(255, 50, 50), tostring(ent) .. ": max render time exceeded!")
|
||||
ent.pac_render_time_exceeded = total_time
|
||||
pac.HideEntityParts(ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.DrawRenderTimeExceeded(ent)
|
||||
cam_Start2D()
|
||||
cam_IgnoreZ(true)
|
||||
local pos_3d = ent:NearestPoint(ent:EyePos() + ent:GetUp()) + Vector(0,0,5)
|
||||
local alpha = math_Clamp(pos_3d:Distance(EyePos()) * -1 + 500, 0, 500) / 500
|
||||
if alpha > 0 then
|
||||
local pos_2d = pos_3d:ToScreen()
|
||||
surface_SetFont("ChatFont")
|
||||
local _, h = surface_GetTextSize("|")
|
||||
|
||||
draw_DrawText(
|
||||
string_format(
|
||||
"pac3 outfit took %.2f/%i ms to render",
|
||||
ent.pac_render_time_exceeded,
|
||||
max_render_time_cvar:GetFloat()
|
||||
),
|
||||
"ChatFont",
|
||||
pos_2d.x,
|
||||
pos_2d.y,
|
||||
Color(255,255,255,alpha * 255),
|
||||
1
|
||||
)
|
||||
local x, y = pos_2d.x, pos_2d.y + h
|
||||
|
||||
local mx, my = input_GetCursorPos()
|
||||
if not vgui_CursorVisible() then
|
||||
mx = ScrW() / 2
|
||||
my = ScrH() / 2
|
||||
end
|
||||
local dist = 200
|
||||
local hovering = mx > x - dist and mx < x + dist and my > y - dist and my < y + dist
|
||||
|
||||
local button = vgui_CursorVisible() and "click" or ("press " .. input_LookupBinding("+use"))
|
||||
draw_DrawText(button .. " here to try again", "ChatFont", x, y, Color(255,255,255,alpha * (hovering and 255 or 100) ), 1)
|
||||
|
||||
if hovering and LocalPlayer():KeyDown(IN_USE) or (vgui_CursorVisible() and input_IsMouseDown(MOUSE_LEFT)) then
|
||||
ent.pac_render_time_exceeded = nil
|
||||
end
|
||||
end
|
||||
|
||||
cam_IgnoreZ(false)
|
||||
cam_End2D()
|
||||
end
|
||||
151
lua/pac3/core/client/owner_name.lua
Normal file
151
lua/pac3/core/client/owner_name.lua
Normal file
@@ -0,0 +1,151 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pac.OwnerNames = {
|
||||
"self",
|
||||
"viewmodel",
|
||||
"hands",
|
||||
"active vehicle",
|
||||
"active weapon",
|
||||
"world",
|
||||
}
|
||||
|
||||
local function find_ent(ent, str)
|
||||
return
|
||||
pac.StringFind(ent:GetClass(), str) or
|
||||
pac.StringFind(ent:GetClass(), str, true) or
|
||||
|
||||
(ent.GetName and pac.StringFind(ent:GetName(), str)) or
|
||||
(ent.GetName and pac.StringFind(ent:GetName(), str, true)) or
|
||||
|
||||
pac.StringFind(ent:GetModel(), str) or
|
||||
pac.StringFind(ent:GetModel(), str, true)
|
||||
end
|
||||
|
||||
local function check_owner(a, b)
|
||||
return a:GetOwner() == b or (not b.CPPIGetOwner or b:CPPIGetOwner() == a or b:CPPIGetOwner() == true)
|
||||
end
|
||||
|
||||
local function calc_entity_crc(ent)
|
||||
local pos = ent:GetPos()
|
||||
local ang = ent:GetAngles()
|
||||
local mdl = ent:GetModel():lower():gsub("\\", "/")
|
||||
local x, y, z = math.Round(pos.x / 10) * 10, math.Round(pos.y / 10) * 10, math.Round(pos.z / 10) * 10
|
||||
local p, _y, r = math.Round(ang.p / 10) * 10, math.Round(ang.y / 10) * 10, math.Round(ang.r / 10) * 10
|
||||
|
||||
local crc = x .. y .. z .. p .. _y .. r .. mdl
|
||||
|
||||
return pac.Hash(crc)
|
||||
end
|
||||
|
||||
SafeRemoveEntity(pac.WorldEntity)
|
||||
|
||||
pac.WorldEntity = NULL
|
||||
|
||||
function pac.GetWorldEntity()
|
||||
if not pac.WorldEntity:IsValid() then
|
||||
local ent = pac.CreateEntity("models/error.mdl")
|
||||
|
||||
ent:SetPos(Vector(0,0,0))
|
||||
|
||||
-- go away ugh
|
||||
ent:SetModelScale(0,0)
|
||||
|
||||
ent.IsPACWorldEntity = true
|
||||
|
||||
pac.WorldEntity = ent
|
||||
end
|
||||
|
||||
return pac.WorldEntity
|
||||
end
|
||||
|
||||
function pac.HandleOwnerName(owner, name, ent, part, check_func)
|
||||
local idx = tonumber(name)
|
||||
|
||||
if idx then
|
||||
ent = Entity(idx)
|
||||
|
||||
if ent:IsValid() then
|
||||
if owner:IsValid() and owner.GetViewModel and ent == owner:GetViewModel() then
|
||||
part:SetOwnerName("viewmodel")
|
||||
return ent
|
||||
end
|
||||
|
||||
if owner:IsValid() and owner.GetHands and ent == owner:GetHands() then
|
||||
part:SetOwnerName("hands")
|
||||
return ent
|
||||
end
|
||||
|
||||
if ent == pac.LocalPlayer then
|
||||
part:SetOwnerName("self")
|
||||
return ent
|
||||
end
|
||||
|
||||
if ent.GetPersistent and ent:GetPersistent() then
|
||||
part:SetOwnerName("persist " .. calc_entity_crc(ent))
|
||||
end
|
||||
|
||||
return ent
|
||||
end
|
||||
|
||||
return pac.GetWorldEntity()
|
||||
end
|
||||
|
||||
if name == "world" or name == "worldspawn" then
|
||||
return pac.GetWorldEntity()
|
||||
end
|
||||
|
||||
if name == "self" then
|
||||
return owner
|
||||
end
|
||||
|
||||
if owner:IsValid() then
|
||||
if name == "active weapon" and owner.GetActiveWeapon and owner:GetActiveWeapon():IsValid() then
|
||||
return owner:GetActiveWeapon()
|
||||
end
|
||||
|
||||
if name == "active vehicle" and owner.GetVehicle and owner:GetVehicle():IsValid() then
|
||||
return owner:GetVehicle()
|
||||
end
|
||||
|
||||
if name == "hands" and owner == pac.LocalPlayer and pac.LocalHands:IsValid() then
|
||||
return pac.LocalHands
|
||||
end
|
||||
|
||||
if name == "hands" and owner.GetHands then
|
||||
return owner:GetHands()
|
||||
end
|
||||
|
||||
if name == "viewmodel" and owner.GetViewModel then
|
||||
return owner:GetViewModel()
|
||||
end
|
||||
|
||||
if IsValid(ent) and (not check_func or check_func(ent)) and check_owner(ent, owner) and find_ent(ent, name) then
|
||||
return ent
|
||||
end
|
||||
|
||||
for _, val in pairs(ents.GetAll()) do
|
||||
if val:IsValid() and (not check_func or check_func(val)) and check_owner(val, owner) and find_ent(val, name) then
|
||||
return val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if name:find("persist ", nil, true) then
|
||||
local crc = name:match("persist (.+)")
|
||||
for _, val in pairs(ents.GetAll()) do
|
||||
if val.GetPersistent and val:GetModel() and val:GetPersistent() and crc == calc_entity_crc(val) then
|
||||
return val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
912
lua/pac3/core/client/part_pool.lua
Normal file
912
lua/pac3/core/client/part_pool.lua
Normal file
@@ -0,0 +1,912 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
|
||||
local render_SetColorModulation = render.SetColorModulation
|
||||
local render_SetBlend = render.SetBlend
|
||||
local render_ModelMaterialOverride = render.ModelMaterialOverride
|
||||
local render_MaterialOverride = render.MaterialOverride
|
||||
local SysTime = SysTime
|
||||
local NULL = NULL
|
||||
local pairs = pairs
|
||||
local force_rendering = false
|
||||
local forced_rendering = false
|
||||
local IsEntity = IsEntity
|
||||
local next = next
|
||||
|
||||
local entMeta = FindMetaTable('Entity')
|
||||
local plyMeta = FindMetaTable('Player')
|
||||
local IsValid = entMeta.IsValid
|
||||
local Alive = plyMeta.Alive
|
||||
|
||||
local function IsActuallyValid(ent)
|
||||
return IsEntity(ent) and pcall(ent.GetPos, ent)
|
||||
end
|
||||
|
||||
local function IsActuallyPlayer(ent)
|
||||
return IsEntity(ent) and pcall(ent.UniqueID, ent)
|
||||
end
|
||||
|
||||
local function IsActuallyRemoved(ent, cb)
|
||||
timer.Simple(0, function()
|
||||
if not ent:IsValid() then
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function pac.ForceRendering(b)
|
||||
force_rendering = b
|
||||
if b then
|
||||
forced_rendering = b
|
||||
end
|
||||
end
|
||||
|
||||
local ent_parts = _G.pac_local_parts or {}
|
||||
local all_parts = _G.pac_all_parts or {}
|
||||
local uid_parts = _G.pac_uid_parts or {}
|
||||
|
||||
if game.SinglePlayer() or (player.GetCount() == 1 and LocalPlayer():IsSuperAdmin()) then
|
||||
_G.pac_local_parts = ent_parts
|
||||
_G.pac_all_parts = all_parts
|
||||
_G.pac_uid_parts = uid_parts
|
||||
end
|
||||
|
||||
local function parts_from_uid(owner_id)
|
||||
return uid_parts[owner_id] or {}
|
||||
end
|
||||
|
||||
local function parts_from_ent(ent)
|
||||
local owner_id = IsValid(ent) and pac.Hash(ent)
|
||||
return uid_parts[owner_id] or {}
|
||||
end
|
||||
|
||||
do
|
||||
local function render_override(ent, type)
|
||||
local parts = ent_parts[ent]
|
||||
|
||||
if parts == nil or next(parts) == nil then
|
||||
pac.UnhookEntityRender(ent)
|
||||
goto CEASE_FUNCTION
|
||||
end
|
||||
|
||||
if type == "update_legacy_bones" then
|
||||
pac.ResetBones(ent)
|
||||
|
||||
for key, part in next, parts do
|
||||
if part:IsValid() then
|
||||
if not part:HasParent() then
|
||||
part:CallRecursive("BuildBonePositions")
|
||||
end
|
||||
else
|
||||
parts[key] = nil
|
||||
end
|
||||
end
|
||||
elseif type == "update" then
|
||||
for key, part in next, parts do
|
||||
if part:IsValid() then
|
||||
if not part:HasParent() then
|
||||
part:CallRecursive("Think")
|
||||
end
|
||||
else
|
||||
parts[key] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
for key, part in next, parts do
|
||||
if part:IsValid() then
|
||||
if not part:HasParent() then
|
||||
if
|
||||
part.OwnerName == "viewmodel" and type == "viewmodel" or
|
||||
part.OwnerName == "hands" and type == "hands" or
|
||||
part.OwnerName ~= "viewmodel" and part.OwnerName ~= "hands" and type ~= "viewmodel" and type ~= "hands"
|
||||
then
|
||||
if not part:IsDrawHidden() then
|
||||
part:CallRecursive("Draw", type)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
parts[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::CEASE_FUNCTION::
|
||||
|
||||
render_SetColorModulation(1, 1, 1)
|
||||
render_SetBlend(1)
|
||||
|
||||
render_MaterialOverride()
|
||||
render_ModelMaterialOverride()
|
||||
end
|
||||
|
||||
local function on_error(msg)
|
||||
ErrorNoHalt(debug.traceback(msg))
|
||||
end
|
||||
|
||||
function pac.RenderOverride(ent, type)
|
||||
if ent.pac_error then return end
|
||||
|
||||
if pac.IsRenderTimeExceeded(ent) then
|
||||
if type == "opaque" then
|
||||
pac.DrawRenderTimeExceeded(ent)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local start = SysTime()
|
||||
local ok, err = xpcall(render_override, on_error, ent, type)
|
||||
pac.RecordRenderTime(ent, type, start)
|
||||
|
||||
if not ok then
|
||||
pac.Message("failed to render ", tostring(ent), ":")
|
||||
|
||||
if ent == pac.LocalPlayer then
|
||||
chat.AddText("your pac3 outfit failed to render!")
|
||||
chat.AddText(err)
|
||||
chat.AddText("hiding your outfit to prevent further errors")
|
||||
end
|
||||
|
||||
ent.pac_error = err
|
||||
table.insert(pac.Errors, err)
|
||||
pac.HideEntityParts(ent)
|
||||
else
|
||||
ent.pac_error = nil
|
||||
end
|
||||
end
|
||||
|
||||
function pac.GetRenderTimeInfo(ent)
|
||||
return ent.pac_rendertime or {}
|
||||
end
|
||||
end
|
||||
|
||||
function pac.HideEntityParts(ent)
|
||||
if ent_parts[ent] and ent.pac_drawing then
|
||||
for _, part in pairs(ent_parts[ent]) do
|
||||
part:HideFromRendering()
|
||||
end
|
||||
|
||||
pac.ResetBones(ent)
|
||||
ent.pac_drawing = false
|
||||
end
|
||||
end
|
||||
|
||||
function pac.ShowEntityParts(ent)
|
||||
if not ent_parts[ent] or ent.pac_shouldnotdraw or ent.pac_ignored then return end
|
||||
|
||||
if not ent.pac_drawing then
|
||||
for _, part in pairs(ent_parts[ent]) do
|
||||
part:ShowFromRendering()
|
||||
end
|
||||
|
||||
pac.ResetBones(ent)
|
||||
ent.pac_drawing = true
|
||||
ent.pac_error = nil
|
||||
elseif ent.pac_fix_show_from_render and ent.pac_fix_show_from_render < SysTime() then
|
||||
for _, part in pairs(ent_parts[ent]) do
|
||||
part:ShowFromRendering()
|
||||
end
|
||||
|
||||
ent.pac_fix_show_from_render = nil
|
||||
end
|
||||
end
|
||||
|
||||
function pac.EnableDrawnEntities(bool)
|
||||
for ent in next, pac.drawn_entities do
|
||||
if ent:IsValid() then
|
||||
if bool then
|
||||
pac.ShowEntityParts(ent)
|
||||
else
|
||||
pac.HideEntityParts(ent)
|
||||
end
|
||||
else
|
||||
pac.drawn_entities[ent] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.HookEntityRender(ent, part)
|
||||
local parts = ent_parts[ent]
|
||||
|
||||
if not parts then
|
||||
parts = {}
|
||||
ent_parts[ent] = parts
|
||||
end
|
||||
|
||||
if parts[part] then
|
||||
return false
|
||||
end
|
||||
|
||||
pac.dprint("hooking render on %s to draw part %s", tostring(ent), tostring(part))
|
||||
|
||||
pac.drawn_entities[ent] = true
|
||||
|
||||
parts[part] = part
|
||||
|
||||
ent.pac_has_parts = true
|
||||
|
||||
part:ShowFromRendering()
|
||||
return true
|
||||
end
|
||||
|
||||
function pac.UnhookEntityRender(ent, part)
|
||||
if part and ent_parts[ent] then
|
||||
ent_parts[ent][part] = nil
|
||||
end
|
||||
|
||||
if (ent_parts[ent] and not next(ent_parts[ent])) or not part then
|
||||
ent_parts[ent] = nil
|
||||
ent.pac_has_parts = nil
|
||||
pac.drawn_entities[ent] = nil
|
||||
|
||||
if ent.pac_bones_once then
|
||||
pac.ResetBones(ent)
|
||||
ent.pac_bones_once = nil
|
||||
end
|
||||
end
|
||||
|
||||
if part then
|
||||
part:HideFromRendering()
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("Think", "events", function()
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if not ent_parts[ply] then continue end
|
||||
if pac.IsEntityIgnored(ply) then continue end
|
||||
|
||||
if Alive(ply) then
|
||||
if ply.pac_revert_ragdoll then
|
||||
ply.pac_revert_ragdoll()
|
||||
ply.pac_revert_ragdoll = nil
|
||||
end
|
||||
continue
|
||||
end
|
||||
|
||||
local rag = ply:GetRagdollEntity()
|
||||
if not IsValid(rag) then
|
||||
pac.HideEntityParts(ply)
|
||||
continue
|
||||
end
|
||||
|
||||
-- so it only runs once
|
||||
if ply.pac_ragdoll == rag then continue end
|
||||
ply.pac_ragdoll = rag
|
||||
rag.pac_ragdoll_owner = ply
|
||||
|
||||
rag = hook.Run("PACChooseDeathRagdoll", ply, rag) or rag
|
||||
|
||||
if ply.pac_death_physics_parts then
|
||||
if ply.pac_physics_died then return end
|
||||
|
||||
for _, part in pairs(parts_from_uid(pac.Hash(ply))) do
|
||||
if part.is_model_part then
|
||||
local ent = part:GetOwner()
|
||||
if ent:IsValid() then
|
||||
rag:SetNoDraw(true)
|
||||
|
||||
part.skip_orient = true
|
||||
|
||||
ent:SetParent(NULL)
|
||||
ent:SetNoDraw(true)
|
||||
ent:PhysicsInitBox(Vector(1,1,1) * -5, Vector(1,1,1) * 5)
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
phys:AddAngleVelocity(VectorRand() * 1000)
|
||||
phys:AddVelocity(ply:GetVelocity() + VectorRand() * 30)
|
||||
phys:Wake()
|
||||
|
||||
function ent.RenderOverride()
|
||||
if part:IsValid() then
|
||||
if not part.HideEntity then
|
||||
part:PreEntityDraw(ent, ent, ent:GetPos(), ent:GetAngles())
|
||||
ent:DrawModel()
|
||||
part:PostEntityDraw(ent, ent, ent:GetPos(), ent:GetAngles())
|
||||
end
|
||||
else
|
||||
ent.RenderOverride = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ply.pac_physics_died = true
|
||||
elseif ply.pac_death_ragdollize or ply.pac_death_ragdollize == nil then
|
||||
|
||||
pac.HideEntityParts(ply)
|
||||
|
||||
for _, part in pairs(ent_parts[ply]) do
|
||||
part:SetOwner(rag)
|
||||
end
|
||||
rag:SetOwner(ply)
|
||||
pac.ShowEntityParts(rag)
|
||||
|
||||
ply.pac_revert_ragdoll = function()
|
||||
ply.pac_ragdoll = nil
|
||||
|
||||
if not ent_parts[ply] then return end
|
||||
|
||||
pac.HideEntityParts(rag)
|
||||
|
||||
for _, part in pairs(ent_parts[ply]) do
|
||||
part:SetOwner(ply)
|
||||
end
|
||||
|
||||
pac.ShowEntityParts(ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if pac.last_flashlight_on ~= pac.LocalPlayer:FlashlightIsOn() then
|
||||
local lamp = ProjectedTexture()
|
||||
|
||||
lamp:SetTexture( "effects/flashlight001" )
|
||||
lamp:SetFarZ( 5000 )
|
||||
lamp:SetColor(Color(0,0,0,255))
|
||||
|
||||
lamp:SetPos( pac.LocalPlayer:EyePos() - pac.LocalPlayer:GetAimVector()*400 )
|
||||
lamp:SetAngles( pac.LocalPlayer:EyeAngles() )
|
||||
lamp:Update()
|
||||
|
||||
hook.Add("PostRender", "pac_flashlight_stuck_fix", function()
|
||||
hook.Remove("PostRender", "pac_flashlight_stuck_fix")
|
||||
lamp:Remove()
|
||||
end)
|
||||
|
||||
pac.last_flashlight_on = pac.LocalPlayer:FlashlightIsOn()
|
||||
end
|
||||
|
||||
for ent in next, pac.drawn_entities do
|
||||
if IsValid(ent) then
|
||||
if ent.pac_drawing and ent:IsPlayer() then
|
||||
|
||||
ent.pac_traceres = util.QuickTrace(ent:EyePos(), ent:GetAimVector() * 32000, {ent, ent:GetVehicle(), ent:GetOwner()})
|
||||
ent.pac_hitpos = ent.pac_traceres.HitPos
|
||||
|
||||
end
|
||||
else
|
||||
pac.drawn_entities[ent] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if pac.next_frame_funcs then
|
||||
for k, fcall in pairs(pac.next_frame_funcs) do
|
||||
fcall()
|
||||
end
|
||||
|
||||
-- table.Empty is also based on undefined behavior
|
||||
-- god damnit
|
||||
for i, key in ipairs(table.GetKeys(pac.next_frame_funcs)) do
|
||||
pac.next_frame_funcs[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if pac.next_frame_funcs_simple and #pac.next_frame_funcs_simple ~= 0 then
|
||||
for i, fcall in ipairs(pac.next_frame_funcs_simple) do
|
||||
fcall()
|
||||
end
|
||||
|
||||
for i = #pac.next_frame_funcs_simple, 1, -1 do
|
||||
pac.next_frame_funcs_simple[i] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("EntityRemoved", "change_owner", function(ent)
|
||||
if IsActuallyValid(ent) then
|
||||
if IsActuallyPlayer(ent) then
|
||||
local parts = parts_from_ent(ent)
|
||||
if next(parts) ~= nil then
|
||||
IsActuallyRemoved(ent, function()
|
||||
for _, part in pairs(parts) do
|
||||
if part.dupe_remove then
|
||||
part:Remove()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
local owner = ent:GetOwner()
|
||||
if IsActuallyPlayer(owner) then
|
||||
local parts = parts_from_ent(owner)
|
||||
if next(parts) ~= nil then
|
||||
IsActuallyRemoved(ent, function()
|
||||
for _, part in pairs(parts) do
|
||||
if part.ClassName == "group" then
|
||||
if part:GetOwnerName() == "hands" then
|
||||
part:UpdateOwnerName()
|
||||
end
|
||||
part:HideInvalidOwners()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
pac.AddHook("OnEntityCreated", "change_owner", function(ent)
|
||||
if not IsActuallyValid(ent) then return end
|
||||
|
||||
local owner = ent:GetOwner()
|
||||
|
||||
if IsActuallyValid(owner) and (not owner:IsPlayer() or IsActuallyPlayer(owner)) then
|
||||
for _, part in pairs(parts_from_ent(owner)) do
|
||||
if part.ClassName == "group" then
|
||||
part:UpdateOwnerName(ent, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function pac.RemovePartsFromUniqueID(uid)
|
||||
for _, part in pairs(parts_from_uid(uid)) do
|
||||
if not part:HasParent() then
|
||||
part:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.UpdatePartsWithMetatable(META)
|
||||
for _, part in pairs(all_parts) do
|
||||
if META.ClassName == part.ClassName then
|
||||
for k, v in pairs(META) do
|
||||
-- update part functions only
|
||||
-- updating variables might mess things up
|
||||
if isfunction(v) then
|
||||
part[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.GetPropertyFromName(func, name, ent_owner)
|
||||
for _, part in pairs(parts_from_ent(ent_owner)) do
|
||||
if part[func] and name == part.Name then
|
||||
return part[func](part)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.RemoveUniqueIDPart(owner_uid, uid)
|
||||
if not uid_parts[owner_uid] then return end
|
||||
uid_parts[owner_uid][uid] = nil
|
||||
end
|
||||
|
||||
function pac.SetUniqueIDPart(owner_uid, uid, part)
|
||||
uid_parts[owner_uid] = uid_parts[owner_uid] or {}
|
||||
uid_parts[owner_uid][uid] = part
|
||||
|
||||
pac.NotifyPartCreated(part)
|
||||
end
|
||||
|
||||
function pac.AddPart(part)
|
||||
all_parts[part.Id] = part
|
||||
end
|
||||
|
||||
function pac.RemovePart(part)
|
||||
all_parts[part.Id] = nil
|
||||
end
|
||||
|
||||
function pac.GetLocalParts()
|
||||
return uid_parts[pac.Hash(pac.LocalPlayer)] or {}
|
||||
end
|
||||
|
||||
function pac.GetPartFromUniqueID(owner_id, id)
|
||||
return uid_parts[owner_id] and uid_parts[owner_id][id] or NULL
|
||||
end
|
||||
|
||||
function pac.FindPartByName(owner_id, str, exclude)
|
||||
if uid_parts[owner_id] then
|
||||
if uid_parts[owner_id][str] then
|
||||
return uid_parts[owner_id][str]
|
||||
end
|
||||
|
||||
for _, part in pairs(uid_parts[owner_id]) do
|
||||
if part == exclude then continue end
|
||||
if part:GetName() == str then
|
||||
return part
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in pairs(uid_parts[owner_id]) do
|
||||
if part == exclude then continue end
|
||||
if pac.StringFind(part:GetName(), str) then
|
||||
return part
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in pairs(uid_parts[owner_id]) do
|
||||
if part == exclude then continue end
|
||||
if pac.StringFind(part:GetName(), str, true) then
|
||||
return part
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
|
||||
function pac.GetLocalPart(id)
|
||||
local owner_id = pac.Hash(pac.LocalPlayer)
|
||||
return uid_parts[owner_id] and uid_parts[owner_id][id] or NULL
|
||||
end
|
||||
|
||||
function pac.RemoveAllParts(owned_only, server)
|
||||
if server and pace then
|
||||
pace.RemovePartOnServer("__ALL__")
|
||||
end
|
||||
|
||||
for _, part in pairs(owned_only and pac.GetLocalParts() or all_parts) do
|
||||
if part:IsValid() then
|
||||
local status, err = pcall(part.Remove, part)
|
||||
if not status then pac.Message('Failed to remove part: ' .. err .. '!') end
|
||||
end
|
||||
end
|
||||
|
||||
if not owned_only then
|
||||
all_parts = {}
|
||||
uid_parts = {}
|
||||
end
|
||||
end
|
||||
|
||||
function pac.UpdateMaterialParts(how, uid, self, val)
|
||||
pac.RunNextFrame("material " .. how .. " " .. self.Id, function()
|
||||
for _, part in pairs(parts_from_uid(uid)) do
|
||||
if how == "update" or how == "remove" then
|
||||
if part.Materialm == val and self ~= part then
|
||||
if how == "update" then
|
||||
part.force_translucent = self.Translucent
|
||||
else
|
||||
part.force_translucent = nil
|
||||
part.Materialm = nil
|
||||
end
|
||||
end
|
||||
elseif how == "show" then
|
||||
if part.Material and part.Material ~= "" and part.Material == val then
|
||||
part:SetMaterial(val)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function pac.NotifyPartCreated(part)
|
||||
local owner_id = part:GetPlayerOwnerId()
|
||||
if not uid_parts[owner_id] then return end
|
||||
|
||||
for _, p in pairs(uid_parts[owner_id]) do
|
||||
p:OnOtherPartCreated(part)
|
||||
|
||||
if part:GetPlayerOwner() == pac.LocalPlayer then
|
||||
pac.CallHook("OnPartCreated", part)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.CallRecursiveOnAllParts(func_name, ...)
|
||||
for _, part in pairs(all_parts) do
|
||||
if part[func_name] then
|
||||
local ret = part[func_name](part, ...)
|
||||
if ret ~= nil then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.CallRecursiveOnOwnedParts(ent, func_name, ...)
|
||||
local owned_parts = parts_from_ent(ent)
|
||||
for _, part in pairs(owned_parts) do
|
||||
if part[func_name] then
|
||||
local ret = part[func_name](part, ...)
|
||||
if ret ~= nil then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pac.EnablePartsByClass(classname, enable)
|
||||
for _, part in pairs(all_parts) do
|
||||
if part.ClassName == classname then
|
||||
part:SetEnabled(enable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cvars.AddChangeCallback("pac_hide_disturbing", function()
|
||||
for _, part in pairs(all_parts) do
|
||||
if part:IsValid() then
|
||||
part:UpdateIsDisturbing()
|
||||
end
|
||||
end
|
||||
end, "PAC3")
|
||||
|
||||
do -- drawing
|
||||
local pac = pac
|
||||
|
||||
local FrameNumber = FrameNumber
|
||||
local RealTime = RealTime
|
||||
local GetConVar = GetConVar
|
||||
local EF_BONEMERGE = EF_BONEMERGE
|
||||
local RENDERMODE_TRANSALPHA = RENDERMODE_TRANSALPHA
|
||||
|
||||
local cvar_distance = CreateClientConVar("pac_draw_distance", "500")
|
||||
|
||||
pac.Errors = {}
|
||||
pac.firstperson_parts = pac.firstperson_parts or {}
|
||||
pac.EyePos = vector_origin
|
||||
pac.drawn_entities = pac.drawn_entities or {}
|
||||
pac.RealTime = 0
|
||||
pac.FrameNumber = 0
|
||||
|
||||
local skip_frames = CreateConVar('pac_optimization_render_once_per_frame', '0', {FCVAR_ARCHIVE}, 'render only once per frame (will break water reflections and vr)')
|
||||
|
||||
local function setup_suppress()
|
||||
local last_framenumber = 0
|
||||
local current_frame = 0
|
||||
local current_frame_count = 0
|
||||
|
||||
return function(force)
|
||||
if not force then
|
||||
if force_rendering then return end
|
||||
if not skip_frames:GetBool() then return end
|
||||
end
|
||||
|
||||
local frame_number = FrameNumber()
|
||||
|
||||
if frame_number == last_framenumber then
|
||||
current_frame = current_frame + 1
|
||||
else
|
||||
last_framenumber = frame_number
|
||||
|
||||
if current_frame_count ~= current_frame then
|
||||
current_frame_count = current_frame
|
||||
end
|
||||
|
||||
current_frame = 1
|
||||
end
|
||||
|
||||
return current_frame < current_frame_count
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local draw_dist = 0
|
||||
local sv_draw_dist = 0
|
||||
local radius = 0
|
||||
local dst = 0
|
||||
local pac_sv_draw_distance
|
||||
|
||||
pac.AddHook("Think", "update_parts", function()
|
||||
-- commonly used variables
|
||||
pac.LocalPlayer = LocalPlayer()
|
||||
pac.LocalViewModel = pac.LocalPlayer:GetViewModel()
|
||||
pac.LocalHands = pac.LocalPlayer:GetHands()
|
||||
pac.RealTime = RealTime()
|
||||
pac.FrameNumber = pac.FrameNumber + 1
|
||||
|
||||
draw_dist = cvar_distance:GetInt()
|
||||
pac_sv_draw_distance = pac_sv_draw_distance or GetConVar("pac_sv_draw_distance")
|
||||
sv_draw_dist = pac_sv_draw_distance:GetFloat()
|
||||
radius = 0
|
||||
|
||||
if draw_dist <= 0 then
|
||||
draw_dist = 32768
|
||||
end
|
||||
|
||||
if sv_draw_dist <= 0 then
|
||||
sv_draw_dist = 32768
|
||||
end
|
||||
|
||||
draw_dist = math.min(sv_draw_dist, draw_dist)
|
||||
|
||||
for ent in next, pac.drawn_entities do
|
||||
if not IsValid(ent) then
|
||||
pac.drawn_entities[ent] = nil
|
||||
goto CONTINUE
|
||||
end
|
||||
|
||||
if ent:IsDormant() then goto CONTINUE end
|
||||
|
||||
pac.ResetRenderTime(ent)
|
||||
|
||||
dst = ent:EyePos():Distance(pac.EyePos)
|
||||
radius = ent:BoundingRadius() * 3 * (ent:GetModelScale() or 1)
|
||||
|
||||
if ent:IsPlayer() or IsValid(ent.pac_ragdoll_owner) then
|
||||
local ply = ent.pac_ragdoll_owner or ent
|
||||
local rag = ply.pac_ragdoll
|
||||
|
||||
if IsValid(rag) and (ply.pac_death_hide_ragdoll or ply.pac_draw_player_on_death) then
|
||||
rag:SetRenderMode(RENDERMODE_TRANSALPHA)
|
||||
|
||||
local c = rag:GetColor()
|
||||
c.a = 0
|
||||
rag:SetColor(c)
|
||||
rag:SetNoDraw(true)
|
||||
if rag:GetParent() ~= ply then
|
||||
rag:SetParent(ent)
|
||||
rag:AddEffects(EF_BONEMERGE)
|
||||
end
|
||||
|
||||
if ply.pac_draw_player_on_death then
|
||||
ply:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
if radius < 32 then
|
||||
radius = 128
|
||||
end
|
||||
elseif not ent:IsNPC() then
|
||||
radius = radius * 4
|
||||
end
|
||||
|
||||
-- if it's a world entity always draw
|
||||
local cond = ent.IsPACWorldEntity
|
||||
|
||||
-- if the entity is the hands, check if we should not draw the localplayer
|
||||
if (ent == pac.LocalHands or ent == pac.LocalViewModel) and not pac.LocalPlayer:ShouldDrawLocalPlayer() then
|
||||
cond = true
|
||||
end
|
||||
|
||||
-- if it's a player, draw if we can see them
|
||||
if not cond and ent == pac.LocalPlayer then
|
||||
cond = ent:ShouldDrawLocalPlayer()
|
||||
end
|
||||
|
||||
-- if the entity has a camera part, draw if it's valid
|
||||
if not cond and ent.pac_camera then
|
||||
cond = ent.pac_camera:IsValid()
|
||||
end
|
||||
|
||||
-- if the condition is not satisified, check draw distance
|
||||
if not cond and ent ~= pac.LocalPlayer then
|
||||
if ent.pac_draw_distance then
|
||||
-- custom draw distance
|
||||
cond = ent.pac_draw_distance <= 0 or dst <= ent.pac_draw_distance
|
||||
else
|
||||
-- otherwise check the cvar
|
||||
cond = dst <= draw_dist
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ent.pac_is_drawing = cond
|
||||
|
||||
if cond then
|
||||
pac.ShowEntityParts(ent)
|
||||
pac.RenderOverride(ent, "update")
|
||||
else
|
||||
if forced_rendering then
|
||||
forced_rendering = false
|
||||
return
|
||||
end
|
||||
|
||||
pac.HideEntityParts(ent)
|
||||
end
|
||||
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
-- we increment the framenumber here because we want to invalidate any FrameNumber caches when we draw
|
||||
-- this prevents functions like movable:GetWorldMatrix() from caching the matrix in the update hook
|
||||
pac.FrameNumber = pac.FrameNumber + 1
|
||||
end)
|
||||
end
|
||||
|
||||
local setupBonesGuard = false
|
||||
function pac.SetupBones(ent)
|
||||
-- Reentrant protection
|
||||
if setupBonesGuard then return end
|
||||
setupBonesGuard = true
|
||||
local ok, err = pcall(ent.SetupBones, ent)
|
||||
setupBonesGuard = false
|
||||
if not ok then error(err) end
|
||||
end
|
||||
|
||||
do
|
||||
local should_suppress = setup_suppress()
|
||||
|
||||
pac.AddHook("PreDrawOpaqueRenderables", "draw_opaque", function(bDrawingDepth, bDrawingSkybox)
|
||||
if should_suppress(true) then return end
|
||||
|
||||
for ent in next, pac.drawn_entities do
|
||||
if ent.pac_is_drawing and ent_parts[ent] and not ent:IsDormant() then
|
||||
pac.RenderOverride(ent, "update_legacy_bones")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local should_suppress = setup_suppress()
|
||||
|
||||
pac.AddHook("PostDrawOpaqueRenderables", "draw_opaque", function(bDrawingDepth, bDrawingSkybox, isDraw3DSkybox)
|
||||
if should_suppress() then return end
|
||||
|
||||
for ent in next, pac.drawn_entities do
|
||||
if ent.pac_is_drawing and ent_parts[ent] and not ent:IsDormant() then
|
||||
|
||||
if isDraw3DSkybox and not ent:GetNW2Bool("pac_in_skybox") then
|
||||
continue
|
||||
end
|
||||
|
||||
pac.RenderOverride(ent, "opaque")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
local should_suppress = setup_suppress()
|
||||
|
||||
pac.AddHook("PostDrawTranslucentRenderables", "draw_translucent", function(bDrawingDepth, bDrawingSkybox, isDraw3DSkybox)
|
||||
if should_suppress() then return end
|
||||
|
||||
for ent in next, pac.drawn_entities do
|
||||
if ent.pac_is_drawing and ent_parts[ent] and not ent:IsDormant() then -- accessing table of NULL doesn't do anything
|
||||
|
||||
if isDraw3DSkybox and not ent:GetNW2Bool("pac_in_skybox") then
|
||||
continue
|
||||
end
|
||||
|
||||
pac.RenderOverride(ent, "translucent")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
pac.AddHook("UpdateAnimation", "update_animation_parts", function(ply)
|
||||
if ply.pac_is_drawing and ent_parts[ply] then -- accessing table of NULL doesn't do anything
|
||||
local parts = ent_parts[ply]
|
||||
for _, part in pairs(parts) do
|
||||
part:CallRecursive("OnUpdateAnimation", ply)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local drawing_viewmodel = false
|
||||
|
||||
pac.AddHook("PostDrawViewModel", "draw_firstperson", function(viewmodelIn, playerIn, weaponIn)
|
||||
if drawing_viewmodel then return end
|
||||
for ent in next, pac.drawn_entities do
|
||||
if IsValid(ent) then
|
||||
if ent.pac_drawing and ent_parts[ent] then
|
||||
drawing_viewmodel=true
|
||||
pac.RenderOverride(ent, "viewmodel")
|
||||
drawing_viewmodel=false
|
||||
end
|
||||
else
|
||||
pac.drawn_entities[ent] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local drawing_hands = false
|
||||
pac.AddHook("PostDrawPlayerHands", "draw_firstperson_hands", function(handsIn, viewmodelIn, playerIn, weaponIn)
|
||||
if drawing_hands then return end
|
||||
for ent in next, pac.drawn_entities do
|
||||
if IsValid(ent) then
|
||||
if ent.pac_drawing and ent_parts[ent] then
|
||||
drawing_hands = true
|
||||
pac.RenderOverride(ent, "hands")
|
||||
drawing_hands = false
|
||||
end
|
||||
else
|
||||
pac.drawn_entities[ent] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
161
lua/pac3/core/client/parts.lua
Normal file
161
lua/pac3/core/client/parts.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
local part_count = 0 -- unique id thing
|
||||
local pairs = pairs
|
||||
|
||||
pac.registered_parts = {}
|
||||
|
||||
local function on_error(msg)
|
||||
ErrorNoHalt(debug.traceback(msg))
|
||||
end
|
||||
|
||||
local function initialize(part, owner)
|
||||
if part.PreInitialize then
|
||||
part:PreInitialize()
|
||||
end
|
||||
|
||||
pac.AddPart(part)
|
||||
|
||||
if owner then
|
||||
part:SetPlayerOwner(owner)
|
||||
end
|
||||
|
||||
part:Initialize()
|
||||
end
|
||||
|
||||
function pac.CreatePart(name, owner, tbl, make_copy, level)
|
||||
level = level or 0
|
||||
name = name or "base"
|
||||
owner = owner or pac.LocalPlayer
|
||||
|
||||
local META = pac.registered_parts[name]
|
||||
|
||||
if not META then
|
||||
pac.Message("Tried to create unknown part: " .. name .. '!')
|
||||
META = pac.registered_parts.base
|
||||
if not META then
|
||||
return NULL
|
||||
end
|
||||
end
|
||||
|
||||
local part = setmetatable({}, META)
|
||||
|
||||
part.Id = part_count
|
||||
part_count = part_count + 1
|
||||
|
||||
if not tbl or not tbl.self.UniqueID then
|
||||
part:SetUniqueID(pac.Hash())
|
||||
end
|
||||
|
||||
part.DefaultVars = {}
|
||||
|
||||
for key in pairs(part.StorableVars) do
|
||||
if key == "UniqueID" then
|
||||
part.DefaultVars[key] = ""
|
||||
else
|
||||
part.DefaultVars[key] = pac.CopyValue(part[key])
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = xpcall(initialize, on_error, part, owner)
|
||||
|
||||
if not ok then
|
||||
part:Remove()
|
||||
|
||||
if part.ClassName ~= "base" then
|
||||
return pac.CreatePart("base", owner, tbl)
|
||||
end
|
||||
end
|
||||
|
||||
if tbl then
|
||||
part:SetTable(tbl, make_copy, level)
|
||||
end
|
||||
|
||||
if not META.GloballyEnabled then
|
||||
part:SetEnabled(false)
|
||||
end
|
||||
|
||||
pac.dprint("creating %s part owned by %s", part.ClassName, tostring(owner))
|
||||
|
||||
return part
|
||||
end
|
||||
|
||||
local reloading = false
|
||||
|
||||
function pac.RegisterPart(META)
|
||||
assert(isstring(META.ClassName), "Part has no classname")
|
||||
assert(istable(META.StorableVars), "Part " .. META.ClassName .. " has no StorableVars")
|
||||
|
||||
do
|
||||
local cvarName = "pac_enable_" .. string.Replace(META.ClassName, " ", "_"):lower()
|
||||
local cvar = CreateClientConVar(cvarName, "1", true)
|
||||
|
||||
cvars.AddChangeCallback(cvarName, function(name, old, new)
|
||||
local enable = tobool(new)
|
||||
META.GloballyEnabled = enable
|
||||
if enable then
|
||||
pac.Message("enabling parts by class " .. META.ClassName)
|
||||
else
|
||||
pac.Message("disabling parts by class " .. META.ClassName)
|
||||
end
|
||||
pac.EnablePartsByClass(META.ClassName, enable)
|
||||
end)
|
||||
|
||||
META.GloballyEnabled = cvar:GetBool()
|
||||
end
|
||||
|
||||
META.__index = META
|
||||
pac.registered_parts[META.ClassName] = META
|
||||
|
||||
if pac.UpdatePartsWithMetatable and _G.pac_ReloadParts then
|
||||
|
||||
if PAC_RESTART then return end
|
||||
if not Entity(1):IsPlayer() then return end
|
||||
if pac.in_initialize then return end
|
||||
|
||||
if not reloading then
|
||||
reloading = true
|
||||
_G.pac_ReloadParts()
|
||||
reloading = false
|
||||
end
|
||||
|
||||
timer.Create("pac_reload", 0, 1, function()
|
||||
for _, other_meta in pairs(pac.registered_parts) do
|
||||
pac.UpdatePartsWithMetatable(other_meta)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.LoadParts()
|
||||
print("loading all parts")
|
||||
|
||||
include("base_part.lua")
|
||||
include("base_movable.lua")
|
||||
include("base_drawable.lua")
|
||||
|
||||
local files = file.Find("pac3/core/client/parts/*.lua", "LUA")
|
||||
for _, name in pairs(files) do
|
||||
include("pac3/core/client/parts/" .. name)
|
||||
end
|
||||
|
||||
local files = file.Find("pac3/core/client/parts/legacy/*.lua", "LUA")
|
||||
for _, name in pairs(files) do
|
||||
include("pac3/core/client/parts/legacy/" .. name)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.GetRegisteredParts()
|
||||
return pac.registered_parts
|
||||
end
|
||||
|
||||
|
||||
385
lua/pac3/core/client/parts/animation.lua
Normal file
385
lua/pac3/core/client/parts/animation.lua
Normal file
@@ -0,0 +1,385 @@
|
||||
--[[
|
||||
| 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 FrameTime = FrameTime
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
local AnimStack
|
||||
AnimStack = {
|
||||
__index = {
|
||||
push = function(self, part)
|
||||
local stack = self.stack
|
||||
|
||||
if #stack == 0 then
|
||||
-- Empty stack
|
||||
table.insert(stack, part)
|
||||
else
|
||||
-- Stop the current animation if it's not self
|
||||
local top = self:getTop()
|
||||
if top ~= part then
|
||||
if top then top:OnStackStop() end
|
||||
|
||||
-- Remove self from stack to move to end and also prevent things from breaking because table.RemoveByValue() only removes the first instance
|
||||
table.RemoveByValue(stack, part)
|
||||
table.insert(stack, part)
|
||||
end
|
||||
end
|
||||
|
||||
part:OnStackStart()
|
||||
end,
|
||||
pop = function(self, part)
|
||||
part:OnStackStop()
|
||||
local stack = self.stack
|
||||
|
||||
-- Remove self from animation stack
|
||||
if table.RemoveByValue(stack, part) == #stack + 1 then
|
||||
-- This was the current animation so play the next in the stack
|
||||
local top = self:getTop()
|
||||
if top then top:OnStackStart() end
|
||||
end
|
||||
end,
|
||||
getTop = function(self)
|
||||
local stack = self.stack
|
||||
local top = stack[#stack]
|
||||
-- Remove invalid parts
|
||||
while top and not top:IsValid() do
|
||||
table.remove(stack)
|
||||
top = stack[#stack]
|
||||
end
|
||||
return top
|
||||
end
|
||||
},
|
||||
__call = function(meta)
|
||||
return setmetatable({
|
||||
stack = {}
|
||||
}, meta)
|
||||
end,
|
||||
get = function(ent)
|
||||
local animStack = ent.pac_animation_stack
|
||||
if not animStack then
|
||||
animStack = AnimStack()
|
||||
ent.pac_animation_stack = animStack
|
||||
end
|
||||
return animStack
|
||||
end
|
||||
}
|
||||
setmetatable(AnimStack, AnimStack)
|
||||
|
||||
PART.ClassName = "animation"
|
||||
PART.ThinkTime = 0
|
||||
PART.Groups = {'entity', 'model', 'modifiers'}
|
||||
PART.Icon = 'icon16/eye.png'
|
||||
|
||||
PART.frame = 0
|
||||
|
||||
BUILDER
|
||||
:StartStorableVars()
|
||||
:GetSet("Loop", true)
|
||||
:GetSet("PingPongLoop", false)
|
||||
:GetSet("SequenceName", "", {enums = function(part) local tbl = {} for k,v in pairs(part:GetSequenceList()) do tbl[v] = v end return tbl end})
|
||||
:GetSet("Rate", 1, {editor_sensitivity = 0.1})
|
||||
:GetSet("Offset", 0)
|
||||
:GetSet("Min", 0)
|
||||
:GetSet("Max", 1)
|
||||
:GetSet("WeaponHoldType", "none", {enums = function(part) return part.ValidHoldTypes end})
|
||||
:GetSet("OwnerCycle", false)
|
||||
:GetSet("InvertFrames", false)
|
||||
:GetSet("ResetOnHide", true)
|
||||
:EndStorableVars()
|
||||
|
||||
local tonumber = tonumber
|
||||
|
||||
PART.ValidHoldTypes =
|
||||
{
|
||||
pistol = ACT_HL2MP_IDLE_PISTOL,
|
||||
smg = ACT_HL2MP_IDLE_SMG1,
|
||||
grenade = ACT_HL2MP_IDLE_GRENADE,
|
||||
ar2 = ACT_HL2MP_IDLE_AR2,
|
||||
shotgun = ACT_HL2MP_IDLE_SHOTGUN,
|
||||
rpg = ACT_HL2MP_IDLE_RPG,
|
||||
physgun = ACT_HL2MP_IDLE_PHYSGUN,
|
||||
crossbow = ACT_HL2MP_IDLE_CROSSBOW,
|
||||
melee = ACT_HL2MP_IDLE_MELEE,
|
||||
slam = ACT_HL2MP_IDLE_SLAM,
|
||||
normal = ACT_HL2MP_IDLE,
|
||||
fist = ACT_HL2MP_IDLE_FIST,
|
||||
melee2 = ACT_HL2MP_IDLE_MELEE2,
|
||||
passive = ACT_HL2MP_IDLE_PASSIVE,
|
||||
knife = ACT_HL2MP_IDLE_KNIFE,
|
||||
duel = ACT_HL2MP_IDLE_DUEL,
|
||||
camera = ACT_HL2MP_IDLE_CAMERA,
|
||||
revolver = ACT_HL2MP_IDLE_REVOLVER,
|
||||
|
||||
zombie = ACT_HL2MP_IDLE_ZOMBIE,
|
||||
magic = ACT_HL2MP_IDLE_MAGIC,
|
||||
meleeangry = ACT_HL2MP_IDLE_MELEE_ANGRY,
|
||||
angry = ACT_HL2MP_IDLE_ANGRY,
|
||||
suitcase = ACT_HL2MP_IDLE_SUITCASE,
|
||||
scared = ACT_HL2MP_IDLE_SCARED,
|
||||
}
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = self:GetSequenceName()
|
||||
|
||||
if str == "" and self:GetWeaponHoldType() ~= "none" then
|
||||
str = self:GetWeaponHoldType()
|
||||
end
|
||||
|
||||
return pac.PrettifyName(str)
|
||||
end
|
||||
|
||||
function PART:GetSequenceList()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
return ent:GetSequenceList()
|
||||
end
|
||||
|
||||
return {"none"}
|
||||
end
|
||||
|
||||
PART.GetSequenceNameList = PART.GetSequenceList
|
||||
|
||||
function PART:OnStackStop()
|
||||
-- Move code from PART:OnHide() to here
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if not self:GetResetOnHide() then
|
||||
self.SequenceCycle = ent:GetCycle()
|
||||
self.storeFrame = self.frame
|
||||
else
|
||||
self.SequenceCycle = nil
|
||||
self.frame = 0
|
||||
end
|
||||
|
||||
if ent.pac_animation_sequences then
|
||||
ent.pac_animation_sequences[self] = nil
|
||||
end
|
||||
|
||||
if ent.pac_animation_holdtypes then
|
||||
ent.pac_animation_holdtypes[self] = nil
|
||||
end
|
||||
|
||||
if not ent:IsPlayer() and self.prevSequence then
|
||||
ent:ResetSequence(self.prevSequence)
|
||||
self.prevSequence = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Stop animation and remove from animation stack
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
AnimStack.get(ent):pop(self)
|
||||
end
|
||||
|
||||
PART.random_seqname = ""
|
||||
|
||||
function PART:SetSequenceName(name)
|
||||
self.SequenceName = name
|
||||
self.random_seqname = table.Random(name:Split(";"))
|
||||
|
||||
if not self:IsHidden() then
|
||||
self:OnShow()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnStackStart()
|
||||
-- Moved code from PART:OnShow() to here
|
||||
self.PlayingSequenceFrom = RealTime()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
self.prevSequence = ent:GetSequence()
|
||||
self.random_seqname = table.Random(self.SequenceName:Split(";"))
|
||||
|
||||
if self.random_seqname ~= "" then
|
||||
local seq = ent:LookupSequence(self.random_seqname) or 0
|
||||
local count = ent:GetSequenceCount() or 0
|
||||
|
||||
if seq < 0 or seq > count or count < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
ent.pac_animation_sequences = ent.pac_animation_sequences or {}
|
||||
ent.pac_animation_sequences[self] = ent.pac_animation_sequences[self] or {}
|
||||
|
||||
local tbl = ent.pac_animation_sequences[self]
|
||||
|
||||
tbl.part = self
|
||||
|
||||
if seq ~= -1 then
|
||||
tbl.seq = seq
|
||||
else
|
||||
seq = tonumber(self.random_seqname) or -1
|
||||
|
||||
if seq ~= -1 then
|
||||
tbl.seq = seq
|
||||
else
|
||||
ent.pac_animation_sequences[self] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if seq ~= -1 then
|
||||
ent:ResetSequence(seq)
|
||||
ent:SetSequence(seq)
|
||||
if not self:GetResetOnHide() then
|
||||
ent:ResetSequenceInfo()
|
||||
|
||||
for i = 1, 10 do
|
||||
ent:FrameAdvance(1)
|
||||
end
|
||||
|
||||
ent:ResetSequenceInfo()
|
||||
end
|
||||
end
|
||||
|
||||
elseif ent:IsPlayer() then
|
||||
local t = self.WeaponHoldType
|
||||
t = t:lower()
|
||||
|
||||
local index = self.ValidHoldTypes[t]
|
||||
|
||||
ent.pac_animation_holdtypes = ent.pac_animation_holdtypes or {}
|
||||
|
||||
if index == nil then
|
||||
ent.pac_animation_holdtypes[self] = nil
|
||||
else
|
||||
local params = {}
|
||||
params[ACT_MP_STAND_IDLE] = index + 0
|
||||
params[ACT_MP_WALK] = index + 1
|
||||
params[ACT_MP_RUN] = index + 2
|
||||
params[ACT_MP_CROUCH_IDLE] = index + 3
|
||||
params[ACT_MP_CROUCHWALK] = index + 4
|
||||
params[ACT_MP_ATTACK_STAND_PRIMARYFIRE] = index + 5
|
||||
params[ACT_MP_ATTACK_CROUCH_PRIMARYFIRE] = index + 5
|
||||
params[ACT_MP_RELOAD_STAND] = index + 6
|
||||
params[ACT_MP_RELOAD_CROUCH] = index + 7
|
||||
params[ACT_MP_JUMP] = index + 8
|
||||
params[ACT_RANGE_ATTACK1] = index + 9
|
||||
params[ACT_MP_SWIM_IDLE] = index + 10
|
||||
params[ACT_MP_SWIM] = index + 11
|
||||
|
||||
-- "normal" jump animation doesn't exist
|
||||
if t == "normal" then
|
||||
params[ACT_MP_JUMP] = ACT_HL2MP_JUMP_SLAM
|
||||
end
|
||||
|
||||
-- these two aren't defined in ACTs for whatever reason
|
||||
if t == "knife" or t == "melee2" then
|
||||
params[ACT_MP_CROUCH_IDLE] = nil
|
||||
end
|
||||
|
||||
params.part = self
|
||||
|
||||
ent.pac_animation_holdtypes[self] = params
|
||||
end
|
||||
end
|
||||
|
||||
if not self:GetResetOnHide() and self.SequenceCycle then
|
||||
ent:SetCycle(self.SequenceCycle)
|
||||
self.SequenceCycle = nil
|
||||
|
||||
if self.storeFrame then
|
||||
self.frame = self.storeFrame
|
||||
self.storeFrame = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Play animation and move to top of animation stack
|
||||
function PART:OnShow()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
AnimStack.get(ent):push(self)
|
||||
end
|
||||
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsPlayer() then
|
||||
self:OnUpdateAnimation(nil)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnUpdateAnimation(ply)
|
||||
if self:IsHiddenCached() then return end
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() or not ent.pac_animation_stack or ent.pac_animation_stack.stack[#ent.pac_animation_stack.stack] ~= self then return end
|
||||
|
||||
-- from UpdateAnimation hook
|
||||
if ply and ent ~= ply then return end
|
||||
|
||||
if not self.random_seqname then return end
|
||||
|
||||
local seq, duration = ent:LookupSequence(self.random_seqname)
|
||||
|
||||
local count = ent:GetSequenceCount() or 0
|
||||
if seq < 0 or seq >= count then
|
||||
-- It's an invalid sequence. Don't bother
|
||||
return
|
||||
end
|
||||
|
||||
if self.OwnerCycle then
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
|
||||
if IsValid(owner) then
|
||||
ent:SetCycle(owner:GetCycle())
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local min = self.Min
|
||||
local max = self.Max
|
||||
local maxmin = max - min
|
||||
|
||||
if min == max then
|
||||
local cycle = min
|
||||
|
||||
if pac.IsNumberValid(cycle) then
|
||||
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local rate = (duration == 0) and 0 or (self.Rate / duration / math.abs(maxmin) * FrameTime())
|
||||
|
||||
if self.PingPongLoop then
|
||||
if self.Loop then
|
||||
self.frame = (self.frame + rate) % 2
|
||||
else
|
||||
self.frame = math.max(math.min(self.frame + rate, 2), 0)
|
||||
end
|
||||
local cycle = min + math.abs(1 - (self.frame + 1 + self.Offset) % 2) * maxmin
|
||||
|
||||
if pac.IsNumberValid(cycle) then
|
||||
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
|
||||
end
|
||||
else
|
||||
if self.Loop then
|
||||
self.frame = (self.frame + rate) % 2
|
||||
else
|
||||
self.frame = math.max(math.min(self.frame + rate, 1), 0)
|
||||
end
|
||||
local cycle = min + (self.frame + self.Offset) % 1 * maxmin
|
||||
|
||||
if pac.IsNumberValid(cycle) then
|
||||
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
240
lua/pac3/core/client/parts/beam.lua
Normal file
240
lua/pac3/core/client/parts/beam.lua
Normal file
@@ -0,0 +1,240 @@
|
||||
--[[
|
||||
| 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 LocalToWorld = LocalToWorld
|
||||
local render_StartBeam = render.StartBeam
|
||||
local render_AddBeam = render.AddBeam
|
||||
local render_EndBeam = render.EndBeam
|
||||
local color_white = color_white
|
||||
local math_sin = math.sin
|
||||
local math_pi = math.pi
|
||||
local Angle = Angle
|
||||
local Lerp = Lerp
|
||||
local Vector = Vector
|
||||
local Color = Color
|
||||
|
||||
-- feel free to use this wherever!
|
||||
do
|
||||
local ax,ay,az = 0,0,0
|
||||
local bx,by,bz = 0,0,0
|
||||
local adx,ady,adz = 0,0,0
|
||||
local bdx,bdy,bdz = 0,0,0
|
||||
|
||||
local frac = 0
|
||||
local wave = 0
|
||||
local bendmult = 0
|
||||
|
||||
local vector = Vector()
|
||||
local color = Color(255, 255, 255, 255)
|
||||
|
||||
function pac.DrawBeam(veca, vecb, dira, dirb, bend, res, width, start_color, end_color, frequency, tex_stretch, tex_scroll, width_bend, width_bend_size, width_start_mul, width_end_mul)
|
||||
|
||||
if not veca or not vecb or not dira or not dirb then return end
|
||||
|
||||
ax = veca.x; ay = veca.y; az = veca.z
|
||||
bx = vecb.x; by = vecb.y; bz = vecb.z
|
||||
|
||||
adx = dira.x; ady = dira.y; adz = dira.z
|
||||
bdx = dirb.x; bdy = dirb.y; bdz = dirb.z
|
||||
|
||||
bend = bend or 10
|
||||
res = math.max(res or 32, 2)
|
||||
width = width or 10
|
||||
start_color = start_color or color_white
|
||||
end_color = end_color or color_white
|
||||
frequency = frequency or 1
|
||||
tex_stretch = tex_stretch or 1
|
||||
width_bend = width_bend or 0
|
||||
width_bend_size = width_bend_size or 1
|
||||
tex_scroll = tex_scroll or 0
|
||||
width_start_mul = width_start_mul or 1
|
||||
width_end_mul = width_end_mul or 1
|
||||
|
||||
render_StartBeam(res + 1)
|
||||
|
||||
for i = 0, res do
|
||||
|
||||
frac = i / res
|
||||
wave = frac * math_pi * frequency
|
||||
bendmult = math_sin(wave) * bend
|
||||
|
||||
vector.x = Lerp(frac, ax, bx) + Lerp(frac, adx * bendmult, bdx * bendmult)
|
||||
vector.y = Lerp(frac, ay, by) + Lerp(frac, ady * bendmult, bdy * bendmult)
|
||||
vector.z = Lerp(frac, az, bz) + Lerp(frac, adz * bendmult, bdz * bendmult)
|
||||
|
||||
color.r = start_color.r == end_color.r and start_color.r or Lerp(frac, start_color.r, end_color.r)
|
||||
color.g = start_color.g == end_color.g and start_color.g or Lerp(frac, start_color.g, end_color.g)
|
||||
color.b = start_color.b == end_color.b and start_color.b or Lerp(frac, start_color.b, end_color.b)
|
||||
color.a = start_color.a == end_color.a and start_color.a or Lerp(frac, start_color.a, end_color.a)
|
||||
|
||||
render_AddBeam(
|
||||
vector,
|
||||
(width + ((math_sin(wave) ^ width_bend_size) * width_bend)) * Lerp(frac, width_start_mul, width_end_mul),
|
||||
(i / tex_stretch) + tex_scroll,
|
||||
color
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
render_EndBeam()
|
||||
end
|
||||
end
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "beam"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/vector.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("Material", "cable/rope")
|
||||
BUILDER:GetSetPart("EndPoint")
|
||||
BUILDER:GetSet("Bend", 10)
|
||||
BUILDER:GetSet("Frequency", 1)
|
||||
BUILDER:GetSet("Resolution", 16)
|
||||
BUILDER:GetSet("Width", 1)
|
||||
BUILDER:GetSet("WidthBend", 0)
|
||||
BUILDER:GetSet("WidthBendSize", 1)
|
||||
BUILDER:GetSet("StartWidthMultiplier", 1)
|
||||
BUILDER:GetSet("EndWidthMultiplier", 1)
|
||||
BUILDER:GetSet("TextureStretch", 1)
|
||||
BUILDER:GetSet("TextureScroll", 0)
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("StartColor", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("EndColor", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("StartAlpha", 1)
|
||||
BUILDER:GetSet("EndAlpha", 1)
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local found = ("/" .. self:GetMaterial()):match(".*/(.+)")
|
||||
return found and pac.PrettifyName(found:gsub("%..+", "")) or "error"
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetMaterial(self.Material)
|
||||
|
||||
self.StartColorC = Color(255, 255, 255, 255)
|
||||
self.EndColorC = Color(255, 255, 255, 255)
|
||||
end
|
||||
|
||||
function PART:SetStartColor(v)
|
||||
self.StartColorC = self.StartColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.StartColorC.r = v.r
|
||||
self.StartColorC.g = v.g
|
||||
self.StartColorC.b = v.b
|
||||
|
||||
self.StartColor = v
|
||||
end
|
||||
|
||||
function PART:SetEndColor(v)
|
||||
self.EndColorC = self.EndColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.EndColorC.r = v.r
|
||||
self.EndColorC.g = v.g
|
||||
self.EndColorC.b = v.b
|
||||
|
||||
self.EndColor = v
|
||||
end
|
||||
|
||||
function PART:SetStartAlpha(n)
|
||||
self.StartColorC = self.StartColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.StartColorC.a = n * 255
|
||||
|
||||
self.StartAlpha = n
|
||||
end
|
||||
|
||||
function PART:SetEndAlpha(n)
|
||||
self.EndColorC = self.EndColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.EndColorC.a = n * 255
|
||||
|
||||
self.EndAlpha = n
|
||||
end
|
||||
|
||||
function PART:FixMaterial()
|
||||
local mat = self.Materialm
|
||||
|
||||
if not mat then return end
|
||||
|
||||
local shader = mat:GetShader()
|
||||
|
||||
if shader == "VertexLitGeneric" or shader == "Cable" then
|
||||
local tex_path = mat:GetString("$basetexture")
|
||||
|
||||
if tex_path then
|
||||
local params = {}
|
||||
|
||||
params["$basetexture"] = tex_path
|
||||
params["$vertexcolor"] = 1
|
||||
params["$vertexalpha"] = 1
|
||||
|
||||
self.Materialm = CreateMaterial(tostring(self) .. "_pac_trail", "UnlitGeneric", params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
self.Material = var
|
||||
|
||||
if not pac.Handleurltex(self, var) then
|
||||
if isstring(var) then
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:FixMaterial()
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
elseif type(var) == "IMaterial" then
|
||||
self.Materialm = var
|
||||
self:FixMaterial()
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local part = self.EndPoint
|
||||
|
||||
if self.Materialm and self.StartColorC and self.EndColorC and part:IsValid() and part.GetWorldPosition then
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
render.SetMaterial(self.Materialm)
|
||||
pac.DrawBeam(
|
||||
pos,
|
||||
part:GetWorldPosition(),
|
||||
|
||||
ang:Forward(),
|
||||
part:GetWorldAngles():Forward(),
|
||||
|
||||
self.Bend,
|
||||
math.Clamp(self.Resolution, 1, 256),
|
||||
self.Width,
|
||||
self.StartColorC,
|
||||
self.EndColorC,
|
||||
self.Frequency,
|
||||
self.TextureStretch,
|
||||
self.TextureScroll,
|
||||
self.WidthBend,
|
||||
self.WidthBendSize,
|
||||
self.StartWidthMultiplier,
|
||||
self.EndWidthMultiplier
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
328
lua/pac3/core/client/parts/bone.lua
Normal file
328
lua/pac3/core/client/parts/bone.lua
Normal file
@@ -0,0 +1,328 @@
|
||||
--[[
|
||||
| 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 NULL = NULL
|
||||
local pairs = pairs
|
||||
local Matrix = Matrix
|
||||
local vector_origin = vector_origin
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
|
||||
for _, v in pairs(ents.GetAll()) do
|
||||
v.pac_bone_setup_data = nil
|
||||
end
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "bone"
|
||||
PART.ClassName = "bone3"
|
||||
PART.Groups = {'entity', 'model'}
|
||||
PART.Icon = 'icon16/connect.png'
|
||||
PART.is_bone_part = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("ScaleChildren", false)
|
||||
BUILDER:GetSet("MoveChildrenToOrigin", false)
|
||||
BUILDER:GetSet("FollowAnglesOnly", false)
|
||||
BUILDER:GetSet("HideMesh", false)
|
||||
BUILDER:GetSet("InvertHideMesh", false)
|
||||
BUILDER:GetSetPart("FollowPart")
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:PropertyOrder("AimPartName")
|
||||
BUILDER:PropertyOrder("Bone")
|
||||
BUILDER:PropertyOrder("Position")
|
||||
BUILDER:PropertyOrder("Angles")
|
||||
BUILDER:PropertyOrder("EyeAngles")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Scale", Vector(1,1,1), {editor_sensitivity = 0.25})
|
||||
BUILDER:PropertyOrder("PositionOffset")
|
||||
BUILDER:PropertyOrder("AngleOffset")
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
return self:GetBone()
|
||||
end
|
||||
|
||||
function PART:SetBone(val)
|
||||
self.Bone = val
|
||||
self.bone_index = self:GetModelBoneIndex(self.Bone)
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self:SetBone(self:GetBone())
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent.pac_bone_parts = ent.pac_bone_parts or {}
|
||||
if not table.HasValue(ent.pac_bone_parts, self) then
|
||||
table.insert(ent.pac_bone_parts, self)
|
||||
end
|
||||
|
||||
if ent.pac_build_bone_id then
|
||||
ent:RemoveCallback("BuildBonePositions", ent.pac_build_bone_id)
|
||||
end
|
||||
|
||||
local id
|
||||
id = ent:AddCallback("BuildBonePositions", function(ent, ...)
|
||||
if not self:IsValid() or not ent.pac_bone_parts or not ent.pac_bone_parts[1] then
|
||||
ent:RemoveCallback("BuildBonePositions", id)
|
||||
return
|
||||
end
|
||||
|
||||
for _, bone in ipairs(ent.pac_bone_parts) do
|
||||
bone:BuildBonePositions2(ent)
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
ent.pac_build_bone_id = id
|
||||
end
|
||||
|
||||
function PART:OnParent()
|
||||
self:OnShow()
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if ent.pac_bone_parts then
|
||||
for i,v in ipairs(ent.pac_bone_parts) do
|
||||
if v == self then
|
||||
table.remove(ent.pac_bone_parts, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local inf_scale = Vector(math.huge, math.huge, math.huge)
|
||||
|
||||
local function get_children_bones(ent, root_index, bone_count, out)
|
||||
ent:SetLOD(0)
|
||||
for child_index = 0, bone_count - 1 do
|
||||
if ent:GetBoneParent(child_index) == root_index then
|
||||
if ent:GetBoneMatrix(child_index) then
|
||||
table.insert(out, child_index)
|
||||
end
|
||||
get_children_bones(ent, child_index, bone_count, out)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_children_bones_cached(ent, root_index)
|
||||
ent.pac_cached_child_bones = ent.pac_cached_child_bones or {}
|
||||
|
||||
if not ent.pac_cached_child_bones[root_index] then
|
||||
ent.pac_cached_child_bones[root_index] = {}
|
||||
get_children_bones(ent, root_index, ent:GetBoneCount(), ent.pac_cached_child_bones[root_index])
|
||||
end
|
||||
|
||||
return ent.pac_cached_child_bones[root_index]
|
||||
end
|
||||
|
||||
local function scale_children(ent, root_index, bone_count, scale, move_to_origin)
|
||||
for child_index = 0, bone_count - 1 do
|
||||
if ent:GetBoneParent(child_index) == root_index then
|
||||
local m = ent:GetBoneMatrix(child_index)
|
||||
|
||||
if m then
|
||||
if move_to_origin then
|
||||
m:SetTranslation(move_to_origin)
|
||||
end
|
||||
|
||||
m:Scale(scale)
|
||||
ent:SetBoneMatrix(child_index, m)
|
||||
end
|
||||
|
||||
scale_children(ent, child_index, bone_count, scale, move_to_origin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local temp_matrix_1 = Matrix()
|
||||
local temp_matrix_2 = Matrix()
|
||||
local temp_vector = Vector()
|
||||
local temp_vector_2 = Vector()
|
||||
local temp_angle = Angle()
|
||||
|
||||
local function temp_vector_add(a, b)
|
||||
temp_vector:Set(a)
|
||||
temp_vector:Add(b)
|
||||
return temp_vector
|
||||
end
|
||||
|
||||
local function temp_vector_scale(a, b)
|
||||
temp_vector_2:Set(a)
|
||||
temp_vector_2:Mul(b)
|
||||
return temp_vector_2
|
||||
end
|
||||
|
||||
|
||||
local function temp_angle_add(a, b)
|
||||
temp_angle:Set(a)
|
||||
temp_angle:Add(b)
|
||||
return temp_angle
|
||||
end
|
||||
|
||||
function PART:BuildBonePositions2(ent)
|
||||
local index = self.bone_index
|
||||
|
||||
if not index then return end
|
||||
|
||||
local world_matrix = ent:GetBoneMatrix(index)
|
||||
|
||||
if not world_matrix then return end
|
||||
|
||||
temp_matrix_2:Set(world_matrix)
|
||||
local unmodified_world_matrix = temp_matrix_2
|
||||
|
||||
self.bone_matrix = self.bone_matrix or Matrix()
|
||||
self.bone_matrix:Set(unmodified_world_matrix)
|
||||
|
||||
if self.FollowPart:IsValid() and self.FollowPart.GetWorldPosition then
|
||||
local pos, ang
|
||||
if self.FollowPart.ClassName == "jiggle" then
|
||||
pos = self.FollowPart.pos
|
||||
ang = self.FollowPart.ang
|
||||
else
|
||||
pos = self.FollowPart:GetWorldPosition()
|
||||
ang = self.FollowPart:GetWorldAngles()
|
||||
end
|
||||
|
||||
if not self.FollowAnglesOnly then
|
||||
world_matrix:SetTranslation(pos)
|
||||
end
|
||||
|
||||
world_matrix:SetAngles(temp_angle_add(ang, self.AngleOffset))
|
||||
world_matrix:Rotate(self.Angles)
|
||||
else
|
||||
world_matrix:Translate(temp_vector_add(self.Position, self.PositionOffset))
|
||||
world_matrix:Rotate(temp_angle_add(self.Angles, self.AngleOffset))
|
||||
end
|
||||
|
||||
local scale = temp_vector_scale(self.Scale, self.Size)
|
||||
|
||||
if self.ScaleChildren or self.MoveChildrenToOrigin then
|
||||
local scale_origin = self.MoveChildrenToOrigin and unmodified_world_matrix:GetTranslation()
|
||||
|
||||
for _, child_index in ipairs(get_children_bones_cached(ent, index)) do
|
||||
local world_matrix = ent:GetBoneMatrix(child_index)
|
||||
if not world_matrix then continue end
|
||||
|
||||
if scale_origin then
|
||||
world_matrix:SetTranslation(scale_origin)
|
||||
end
|
||||
|
||||
if self.ScaleChildren then
|
||||
world_matrix:Scale(scale)
|
||||
end
|
||||
|
||||
ent:SetBoneMatrix(child_index, world_matrix)
|
||||
end
|
||||
end
|
||||
|
||||
local parent_world_matrix = world_matrix
|
||||
unmodified_world_matrix:Invert()
|
||||
local last_inverted_world_matrix = unmodified_world_matrix
|
||||
|
||||
for _, child_index in ipairs(get_children_bones_cached(ent, index)) do
|
||||
local child_world_matrix = ent:GetBoneMatrix(child_index)
|
||||
if not child_world_matrix then continue end
|
||||
|
||||
temp_matrix_1:Set(parent_world_matrix)
|
||||
temp_matrix_1:Mul(last_inverted_world_matrix)
|
||||
temp_matrix_1:Mul(child_world_matrix)
|
||||
|
||||
local world_matrix = temp_matrix_1
|
||||
|
||||
ent:SetBoneMatrix(child_index, world_matrix)
|
||||
|
||||
parent_world_matrix = world_matrix
|
||||
|
||||
child_world_matrix:Invert()
|
||||
last_inverted_world_matrix = child_world_matrix
|
||||
end
|
||||
|
||||
world_matrix:Scale(scale)
|
||||
|
||||
ent:SetBoneMatrix(index, world_matrix)
|
||||
|
||||
if self.HideMesh then
|
||||
local inf_scale = inf_scale
|
||||
|
||||
if ent.GetRagdollEntity and ent:GetRagdollEntity():IsValid() then
|
||||
inf_scale = vector_origin
|
||||
end
|
||||
|
||||
ent.pac_inf_scale = true
|
||||
|
||||
if self.InvertHideMesh then
|
||||
local count = ent:GetBoneCount()
|
||||
|
||||
for i = 0, count - 1 do
|
||||
if i ~= index then
|
||||
ent:ManipulateBoneScale(i, inf_scale)
|
||||
end
|
||||
end
|
||||
else
|
||||
ent:ManipulateBoneScale(index, inf_scale)
|
||||
end
|
||||
else
|
||||
ent.pac_inf_scale = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:GetBonePosition()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return Vector(), Angle() end
|
||||
|
||||
if not self.bone_index then return ent:GetPos(), ent:GetAngles() end
|
||||
|
||||
local m = ent:GetBoneMatrix(self.bone_index)
|
||||
if not m then return ent:GetPos(), ent:GetAngles() end
|
||||
|
||||
local pos = m:GetTranslation()
|
||||
local ang = m:GetAngles()
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
function PART:GetBoneMatrix()
|
||||
return self.bone_matrix or Matrix()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
|
||||
pac.AddHook("OnEntityCreated", "hide_mesh_no_crash", function(ent)
|
||||
local ply = ent:GetRagdollOwner()
|
||||
if ply:IsPlayer() and ply.pac_inf_scale then
|
||||
for i = 0, ply:GetBoneCount() - 1 do
|
||||
local scale = ply:GetManipulateBoneScale(i)
|
||||
if scale == inf_scale then
|
||||
scale = Vector(0,0,0)
|
||||
end
|
||||
ply:ManipulateBoneScale(i, scale)
|
||||
end
|
||||
end
|
||||
end)
|
||||
86
lua/pac3/core/client/parts/camera.lua
Normal file
86
lua/pac3/core/client/parts/camera.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
--[[
|
||||
| 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 LerpAngle = LerpAngle
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.ClassName = "camera"
|
||||
PART.Group = 'entity'
|
||||
PART.Icon = 'icon16/camera.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("EyeAnglesLerp", 1)
|
||||
BUILDER:GetSet("DrawViewModel", false)
|
||||
|
||||
BUILDER:GetSet("NearZ", -1)
|
||||
BUILDER:GetSet("FarZ", -1)
|
||||
BUILDER:GetSet("FOV", -1)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
for i, ply in ipairs(player.GetAll()) do
|
||||
ply.pac_cameras = nil
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
owner.pac_cameras = owner.pac_cameras or {}
|
||||
owner.pac_cameras[self] = self
|
||||
end
|
||||
|
||||
function PART:CalcView(_, _, eyeang, fov, nearz, farz)
|
||||
local pos, ang = self:GetDrawPosition(nil, true)
|
||||
|
||||
ang = LerpAngle(self.EyeAnglesLerp, ang, eyeang)
|
||||
|
||||
if self.NearZ > 0 then
|
||||
nearz = self.NearZ
|
||||
end
|
||||
|
||||
if self.FarZ > 0 then
|
||||
farz = self.FarZ
|
||||
end
|
||||
|
||||
if self.FOV > 0 then
|
||||
fov = self.FOV
|
||||
end
|
||||
|
||||
return pos, ang, fov, nearz, farz
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
|
||||
local temp = {}
|
||||
|
||||
pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
|
||||
if not ply.pac_cameras then return end
|
||||
if ply:GetViewEntity() ~= ply then return end
|
||||
|
||||
for _, part in pairs(ply.pac_cameras) do
|
||||
if part:IsValid() then
|
||||
part:CalcShowHide()
|
||||
|
||||
if not part:IsHidden() then
|
||||
pos, ang, fov, nearz, farz = part:CalcView(ply, pos, ang, fov, nearz, farz)
|
||||
temp.origin = pos
|
||||
temp.angles = ang
|
||||
temp.fov = fov
|
||||
temp.znear = nearz
|
||||
temp.zfar = farz
|
||||
temp.drawviewer = not part.DrawViewModel
|
||||
return temp
|
||||
end
|
||||
else
|
||||
ply.pac_cameras[part] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
94
lua/pac3/core/client/parts/censor.lua
Normal file
94
lua/pac3/core/client/parts/censor.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "woohoo"
|
||||
PART.Group = "effects"
|
||||
PART.Icon = "icon16/webcam_delete.png"
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Resolution", 8)
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("FixedSize", true)
|
||||
BUILDER:GetSet("BlurFiltering", false)
|
||||
BUILDER:GetSet("Translucent", true)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local render_ReadPixel = render.ReadPixel
|
||||
local surface_SetDrawColor = surface.SetDrawColor
|
||||
local surface_DrawRect = surface.DrawRect
|
||||
local render_CapturePixels = render.CapturePixels
|
||||
|
||||
local x2, y2
|
||||
local r,g,b
|
||||
|
||||
function PART:SetSize(size)
|
||||
self.Size = math.Clamp(size, 1, 32)
|
||||
end
|
||||
|
||||
local function create_rt(self)
|
||||
self.rt = GetRenderTargetEx(
|
||||
"pac3_woohoo_rt_" .. math.Round(self.Resolution) .. "_" .. tostring(self.BlurFiltering),
|
||||
self.Resolution,
|
||||
self.Resolution,
|
||||
RT_SIZE_NO_CHANGE,
|
||||
MATERIAL_RT_DEPTH_NONE,
|
||||
self.BlurFiltering and 2 or 1, -- TEXTUREFLAGS_POINTSAMPLE,
|
||||
CREATERENDERTARGETFLAGS_AUTOMIPMAP,
|
||||
IMAGE_FORMAT_RGB565
|
||||
)
|
||||
|
||||
collectgarbage()
|
||||
end
|
||||
|
||||
function PART:SetBlurFiltering(b)
|
||||
self.BlurFiltering = b
|
||||
create_rt(self)
|
||||
end
|
||||
|
||||
function PART:SetResolution(num)
|
||||
local old = self.Resolution
|
||||
self.Resolution = math.Clamp(num, 4, 1024)
|
||||
|
||||
if not old or math.Round(old) ~= math.Round(self.Resolution) then
|
||||
create_rt(self)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if not self.rt then create_rt(self) end
|
||||
|
||||
render.CopyTexture(render.GetScreenEffectTexture(), self.rt)
|
||||
|
||||
cam.Start2D()
|
||||
|
||||
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
local spos = pos:ToScreen()
|
||||
local size = self.Size
|
||||
|
||||
if self.FixedSize then
|
||||
size = size / pos:Distance(pac.EyePos) * 100
|
||||
end
|
||||
|
||||
size = size * 64
|
||||
|
||||
local x, y, w, h = spos.x-size, spos.y-size, spos.x + size, spos.y + size
|
||||
|
||||
render.SetScissorRect(x,y,w,h, true)
|
||||
render.DrawTextureToScreenRect(self.rt, 0, 0, ScrW(), ScrH())
|
||||
render.SetScissorRect(0, 0, 0, 0, false)
|
||||
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
68
lua/pac3/core/client/parts/clip.lua
Normal file
68
lua/pac3/core/client/parts/clip.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
--[[
|
||||
| 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 render_EnableClipping = render.EnableClipping
|
||||
local render_PushCustomClipPlane = render.PushCustomClipPlane
|
||||
local LocalToWorld = LocalToWorld
|
||||
local IsEntity = IsEntity
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "clip"
|
||||
PART.ClassName = "clip2"
|
||||
PART.Groups = {'model', 'modifiers'}
|
||||
PART.Icon = 'icon16/cut.png'
|
||||
|
||||
function PART:OnParent(part)
|
||||
if not part.AddModifier then return end
|
||||
part:AddModifier(self)
|
||||
|
||||
|
||||
-- this is only really for halos..
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
function ent.pacDrawModel(ent)
|
||||
self:PreOnDraw()
|
||||
ent:DrawModel()
|
||||
self:PostOnDraw()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnUnParent(part)
|
||||
if not part:IsValid() then return end
|
||||
if not part.RemoveModifier then return end
|
||||
|
||||
part:RemoveModifier(self)
|
||||
end
|
||||
|
||||
do
|
||||
local bclip
|
||||
|
||||
function PART:PreOnDraw()
|
||||
bclip = render_EnableClipping(true)
|
||||
|
||||
local pos, ang = LocalToWorld(self.Position + self.PositionOffset, self:CalcAngles(self.Angles + self.AngleOffset), self:GetBonePosition())
|
||||
local normal = ang:Forward()
|
||||
|
||||
render_PushCustomClipPlane(normal, normal:Dot(pos))
|
||||
end
|
||||
|
||||
local render_PopCustomClipPlane = render.PopCustomClipPlane
|
||||
|
||||
function PART:PostOnDraw()
|
||||
render_PopCustomClipPlane()
|
||||
|
||||
render_EnableClipping(bclip)
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
102
lua/pac3/core/client/parts/command.lua
Normal file
102
lua/pac3/core/client/parts/command.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "command"
|
||||
|
||||
PART.Group = "advanced"
|
||||
PART.Icon = "icon16/application_xp_terminal.png"
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("String", "", {editor_panel = "string"})
|
||||
BUILDER:GetSet("UseLua", false)
|
||||
BUILDER:GetSet("ExecuteOnWear", false)
|
||||
BUILDER:GetSet("ExecuteOnShow", true)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local sv_allowcslua = GetConVar("sv_allowcslua")
|
||||
local function canRunLua()
|
||||
return sv_allowcslua:GetBool() or pac.AllowClientsideLUA
|
||||
end
|
||||
|
||||
function PART:OnWorn()
|
||||
if self:GetExecuteOnWear() then
|
||||
self:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering and self:GetExecuteOnShow() then
|
||||
self:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetUseLua(b)
|
||||
self.UseLua = b
|
||||
self:SetString(self:GetString())
|
||||
end
|
||||
|
||||
function PART:SetString(str)
|
||||
if self.UseLua and canRunLua() and self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
self.func = CompileString(str, "pac_event")
|
||||
end
|
||||
|
||||
self.String = str
|
||||
end
|
||||
|
||||
function PART:GetCode()
|
||||
return self.String
|
||||
end
|
||||
|
||||
function PART:SetCode(str)
|
||||
self.String = str
|
||||
end
|
||||
|
||||
function PART:ShouldHighlight(str)
|
||||
return _G[str] ~= nil
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
if self.UseLua then
|
||||
return ("lua: " .. self.String)
|
||||
end
|
||||
return "command: " .. self.String
|
||||
end
|
||||
|
||||
function PART:Execute()
|
||||
local ent = self:GetPlayerOwner()
|
||||
|
||||
if ent == pac.LocalPlayer then
|
||||
if self.UseLua and self.func then
|
||||
if canRunLua() then
|
||||
local status, err = pcall(self.func)
|
||||
|
||||
if not status then
|
||||
self:SetError(err)
|
||||
ErrorNoHalt(err .. "\n")
|
||||
end
|
||||
else
|
||||
local msg = "clientside lua is disabled (sv_allowcslua 0)"
|
||||
self:SetError(msg)
|
||||
pac.Message(tostring(self) .. " - ".. msg)
|
||||
end
|
||||
else
|
||||
if hook.Run("PACCanRunConsoleCommand", self.String) == false then return end
|
||||
if IsConCommandBlocked(self.String) then
|
||||
self:SetError("Concommand is blocked")
|
||||
return
|
||||
end
|
||||
ent:ConCommand(self.String)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
195
lua/pac3/core/client/parts/custom_animation.lua
Normal file
195
lua/pac3/core/client/parts/custom_animation.lua
Normal file
@@ -0,0 +1,195 @@
|
||||
--[[
|
||||
| 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 animations = pac.animations
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "custom_animation"
|
||||
|
||||
PART.Group = 'advanced'
|
||||
PART.Icon = 'icon16/film.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("URL", "")
|
||||
BUILDER:GetSet("Data", "")
|
||||
BUILDER:GetSet("StopOnHide", true)
|
||||
BUILDER:GetSet("StopOtherAnimations", false)
|
||||
BUILDER:GetSet("AnimationType", "sequence", {enums = {
|
||||
gesture = "gesture",
|
||||
posture = "posture",
|
||||
sequence = "sequence",
|
||||
stance = "stance",
|
||||
}})
|
||||
BUILDER:GetSet("Interpolation", "cosine", {enums = {
|
||||
linear = "linear",
|
||||
cosine = "cosine",
|
||||
cubic = "cubic",
|
||||
none = "none",
|
||||
}})
|
||||
BUILDER:GetSet("Rate", 1)
|
||||
BUILDER:GetSet("BonePower", 1)
|
||||
BUILDER:GetSet("Offset", 0)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
return pac.PrettifyName(("/".. self:GetURL()):match(".+/(.-)%.")) or "no anim"
|
||||
end
|
||||
|
||||
function PART:GetAnimID()
|
||||
return "pac_anim_" .. (self:GetPlayerOwnerId() or "null") .. "_" .. self:GetUniqueID()
|
||||
end
|
||||
|
||||
function PART:GetLuaAnimation()
|
||||
local owner = self:GetOwner()
|
||||
if owner:IsValid() and owner.pac_animations then
|
||||
return owner.pac_animations[self:GetAnimID()]
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetRate(num)
|
||||
self.Rate = num
|
||||
local anim = self:GetLuaAnimation()
|
||||
if anim then
|
||||
anim.TimeScale = self.Rate
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetBonePower(num)
|
||||
self.BonePower = num
|
||||
local anim = self:GetLuaAnimation()
|
||||
if anim then
|
||||
anim.Power = self.BonePower
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetInterpolation(mode)
|
||||
self.Interpolation = mode
|
||||
local anim = self:GetLuaAnimation()
|
||||
if anim then
|
||||
anim.Interpolation = mode
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetOffset(num)
|
||||
self.Offset = num
|
||||
local anim = self:GetLuaAnimation()
|
||||
if anim then
|
||||
anim.Offset = num
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetURL(url)
|
||||
self.URL = url
|
||||
self:SetError()
|
||||
|
||||
if url:find("http") then
|
||||
pac.HTTPGet(url, function(str)
|
||||
local tbl = util.JSONToTable(str)
|
||||
if not tbl then
|
||||
pac.Message("Animation failed to parse from ", url)
|
||||
self:SetError("Animation failed to parse from " .. url)
|
||||
return
|
||||
end
|
||||
|
||||
animations.ConvertOldData(tbl)
|
||||
|
||||
self:SetAnimationType(tbl.Type)
|
||||
self:SetInterpolation(tbl.Interpolation)
|
||||
|
||||
animations.RegisterAnimation(self:GetAnimID(), tbl)
|
||||
|
||||
if pace and pace.timeline.IsActive() and pace.timeline.animation_part == self then
|
||||
pace.timeline.Load(tbl)
|
||||
end
|
||||
end,
|
||||
function(err)
|
||||
if self:IsValid() and pac.LocalPlayer == self:GetPlayerOwner() and pace and pace.IsActive() then
|
||||
if pace and pace.current_part == self and not IsValid(pace.BusyWithProperties) then
|
||||
pace.MessagePrompt(err, "HTTP Request Failed for " .. url, "OK")
|
||||
else
|
||||
local msg = "HTTP Request failed for " .. url .. " - " .. err
|
||||
self:SetError(msg)
|
||||
pac.Message(Color(0, 255, 0), "[animation] ", Color(255, 255, 255), msg)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetData(str)
|
||||
self.Data = str
|
||||
if str then
|
||||
local tbl = util.JSONToTable(str)
|
||||
if tbl then
|
||||
|
||||
if isnumber(tbl.Type) then
|
||||
animations.ConvertOldData(tbl)
|
||||
self:SetAnimationType(tbl.Type)
|
||||
self:SetInterpolation(tbl.Interpolation)
|
||||
end
|
||||
|
||||
animations.RegisterAnimation(self:GetAnimID(), tbl)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if not animations.GetRegisteredAnimations()[self:GetAnimID()] then
|
||||
self:SetURL(self:GetURL())
|
||||
end
|
||||
|
||||
if owner:IsValid() then
|
||||
if not self:GetStopOnHide() then
|
||||
if animations.GetRegisteredAnimations()[self:GetAnimID()] then
|
||||
animations.StopEntityAnimation(owner, self:GetAnimID())
|
||||
end
|
||||
end
|
||||
animations.SetEntityAnimation(owner, self:GetAnimID())
|
||||
self:SetOffset(self:GetOffset())
|
||||
self:SetRate(self:GetRate())
|
||||
self:SetBonePower(self:GetBonePower())
|
||||
self:SetInterpolation(self:GetInterpolation())
|
||||
if self.StopOtherAnimations and owner.pac_animations then
|
||||
for id in pairs(owner.pac_animations) do
|
||||
if id ~= self:GetAnimID() then
|
||||
animations.StopEntityAnimation(owner, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
--stop animation
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() and self:GetStopOnHide() then
|
||||
if animations.GetRegisteredAnimations()[self:GetAnimID()] then
|
||||
animations.StopEntityAnimation(owner, self:GetAnimID())
|
||||
end
|
||||
animations.ResetEntityBoneMatrix(owner)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
animations.StopEntityAnimation(owner, self:GetAnimID())
|
||||
animations.ResetEntityBoneMatrix(owner)
|
||||
end
|
||||
|
||||
animations.GetRegisteredAnimations()[self:GetAnimID()] = nil
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
59
lua/pac3/core/client/parts/decal.lua
Normal file
59
lua/pac3/core/client/parts/decal.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--!lc util.DecalEx(Material("sprites/key_0"), this:IsValid() and this or Entity(0), there + trace.Normal, -trace.HitNormal, Color(255,255,255,255), 0.5,0.5)
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.ClassName = "decal"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/paintbrush.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
--BUILDER:GetSet("Width", 1)
|
||||
--BUILDER:GetSet("Height", 1)
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
BUILDER:GetSet("Material", "")
|
||||
BUILDER:GetSet("IgnoreOwner", true)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
self.Material = var
|
||||
if not pac.Handleurltex(self, var) then
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
if self.Materialm then
|
||||
local filter
|
||||
if self.IgnoreOwner then
|
||||
filter = ents.FindInSphere(pos, 100)
|
||||
end
|
||||
local data = util.TraceLine({start = pos, endpos = pos + (ang:Forward() * 1000), filter = filter})
|
||||
|
||||
if data.Hit then
|
||||
|
||||
util.DecalEx(
|
||||
self.Materialm,
|
||||
data.Entity:IsValid() and data.Entity or Entity(0),
|
||||
data.HitPos + data.Normal,
|
||||
-data.HitNormal,
|
||||
Color(self.Color.x, self.Color.y, self.Color.z, self.Alpha*255),
|
||||
1, 1 -- they don't do anything?
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
227
lua/pac3/core/client/parts/effect.lua
Normal file
227
lua/pac3/core/client/parts/effect.lua
Normal file
@@ -0,0 +1,227 @@
|
||||
--[[
|
||||
| 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 CurTime = CurTime
|
||||
local ParticleEffect = ParticleEffect
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "effect"
|
||||
PART.Groups = {'effects', 'model', 'entity'}
|
||||
PART.Icon = 'icon16/wand.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Effect", "default", {enums = function() return pac.particle_list end})
|
||||
BUILDER:GetSet("Loop", true)
|
||||
BUILDER:GetSet("Follow", true)
|
||||
BUILDER:GetSet("Rate", 1, {editor_sensitivity = 0.1})
|
||||
BUILDER:GetSet("UseParticleTracer", false)
|
||||
|
||||
BUILDER:GetSetPart("PointA")
|
||||
BUILDER:GetSetPart("PointB")
|
||||
BUILDER:GetSetPart("PointC")
|
||||
BUILDER:GetSetPart("PointD")
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
BUILDER:RemoveProperty("Translucent")
|
||||
PART.Translucent = false -- otherwise OnDraw won't be called
|
||||
|
||||
local BaseClass_GetOwner = PART.GetOwner
|
||||
|
||||
function PART:GetNiceName()
|
||||
return pac.PrettifyName(self:GetEffect())
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetEffect(self.Effect)
|
||||
|
||||
if not pac.particle_list then
|
||||
local found = {}
|
||||
|
||||
for file_name in pairs(pac_loaded_particle_effects) do
|
||||
local ok, err = pcall(function()
|
||||
local data = file.Read("particles/"..file_name, "GAME", "b")
|
||||
if data then
|
||||
for str in data:gmatch("\3%c([%a_]+)%c") do
|
||||
if #str > 1 then
|
||||
found[str] = str
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
local msg = "unable to parse particle file " .. file_name .. ": " .. err
|
||||
self:SetError(msg)
|
||||
pac.Message(Color(255, 50, 50), msg)
|
||||
end
|
||||
end
|
||||
|
||||
pac.particle_list = found
|
||||
end
|
||||
end
|
||||
|
||||
PART.last_spew = 0
|
||||
|
||||
if not pac_loaded_particle_effects then
|
||||
pac_loaded_particle_effects = {}
|
||||
|
||||
for _, file_name in pairs(file.Find("particles/*.pcf", "GAME")) do
|
||||
if not pac_loaded_particle_effects[file_name] and not pac.BlacklistedParticleSystems[file_name:lower()] then
|
||||
game.AddParticles("particles/" .. file_name)
|
||||
end
|
||||
|
||||
pac_loaded_particle_effects[file_name] = true
|
||||
end
|
||||
end
|
||||
|
||||
local already = {}
|
||||
local alreadyServer = {}
|
||||
local function pac_request_precache(name)
|
||||
if already[name] then return end
|
||||
already[name] = true
|
||||
PrecacheParticleSystem(name)
|
||||
net.Start("pac_request_precache")
|
||||
net.WriteString(name)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function PART:SetEffect(name)
|
||||
self.waitingForServer = true
|
||||
self.Effect = name
|
||||
self.Ready = alreadyServer[name] or false
|
||||
|
||||
if not alreadyServer[name] then
|
||||
pac_request_precache(name)
|
||||
else
|
||||
self.waitingForServer = false
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("pac_EffectPrecached", "pac_Effects", function(name)
|
||||
if alreadyServer[name] then return end
|
||||
alreadyServer[name] = true
|
||||
pac.dprint("effect %q precached!", name)
|
||||
pac.CallRecursiveOnAllParts("OnEffectPrecached", name)
|
||||
end)
|
||||
|
||||
function PART:OnEffectPrecached(name)
|
||||
if self.Effect == name then
|
||||
self.Ready = true
|
||||
self.waitingForServer = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if not self.Ready then
|
||||
if not self.waitingForServer then
|
||||
self:SetEffect(self.Effect)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and self.Loop then
|
||||
local time = CurTime()
|
||||
if self.last_spew < time then
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
ent:StopParticles()
|
||||
ent:StopParticleEmission()
|
||||
self:Emit(pos, ang)
|
||||
self.last_spew = time + math.max(self.Rate, 0.1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent:StopParticles()
|
||||
ent:StopParticleEmission()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if from_rendering then
|
||||
self:Emit(self:GetDrawPosition())
|
||||
end
|
||||
end
|
||||
|
||||
function PART:Emit(pos, ang)
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if not self.Effect then
|
||||
ent:StopParticles()
|
||||
ent:StopParticleEmission()
|
||||
return
|
||||
end
|
||||
|
||||
if self.UseParticleTracer and self.PointA:IsValid() then
|
||||
local ent2 = self.PointA.Entity and self.PointA.Entity or self.PointA:GetOwner()
|
||||
|
||||
util.ParticleTracerEx(
|
||||
self.Effect,
|
||||
ent:GetPos(),
|
||||
ent2:GetPos(),
|
||||
true,
|
||||
ent:EntIndex(),
|
||||
0
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
if self.PointA:IsValid() then
|
||||
local points = {}
|
||||
|
||||
table.insert(points, {
|
||||
entity = self.PointA.Entity and self.PointA.Entity or self.PointA:GetOwner(),
|
||||
attachtype = PATTACH_ABSORIGIN_FOLLOW,
|
||||
})
|
||||
|
||||
if self.PointB:IsValid() then
|
||||
table.insert(points, {
|
||||
entity = self.PointB.Entity and self.PointB.Entity or self.PointB:GetOwner(),
|
||||
attachtype = PATTACH_ABSORIGIN_FOLLOW,
|
||||
})
|
||||
end
|
||||
|
||||
if self.PointC:IsValid() then
|
||||
table.insert(points, {
|
||||
entity = self.PointC.Entity and self.PointC.Entity or self.PointC:GetOwner(),
|
||||
attachtype = PATTACH_ABSORIGIN_FOLLOW,
|
||||
})
|
||||
end
|
||||
|
||||
if self.PointD:IsValid() then
|
||||
table.insert(points, {
|
||||
entity = self.PointD.Entity and self.PointD.Entity or self.PointD:GetOwner(),
|
||||
attachtype = PATTACH_ABSORIGIN_FOLLOW,
|
||||
})
|
||||
end
|
||||
|
||||
ent:CreateParticleEffect(self.Effect, points)
|
||||
elseif self.Follow then
|
||||
ent:StopParticles()
|
||||
ent:StopParticleEmission()
|
||||
CreateParticleSystem(ent, self.Effect, PATTACH_ABSORIGIN_FOLLOW, 0)
|
||||
else
|
||||
ent:StopParticles()
|
||||
ent:StopParticleEmission()
|
||||
ParticleEffect(self.Effect, pos, ang, ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
2220
lua/pac3/core/client/parts/event.lua
Normal file
2220
lua/pac3/core/client/parts/event.lua
Normal file
File diff suppressed because it is too large
Load Diff
205
lua/pac3/core/client/parts/faceposer.lua
Normal file
205
lua/pac3/core/client/parts/faceposer.lua
Normal file
@@ -0,0 +1,205 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "faceposer"
|
||||
|
||||
PART.FriendlyName = "face poser"
|
||||
PART.Icon = 'icon16/monkey.png'
|
||||
PART.Group = 'entity'
|
||||
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:GetSet("Preset", "", {enums = function(part)
|
||||
local ent = part:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
local maps = {}
|
||||
|
||||
local toolgun = {}
|
||||
for i = 0, 255 do
|
||||
local name = ent:GetFlexName(i)
|
||||
if name then
|
||||
toolgun[name] = GetConVar("faceposer_flex" .. i):GetFloat()
|
||||
end
|
||||
end
|
||||
|
||||
maps.toolgun = util.TableToJSON({
|
||||
scale = GetConVar("faceposer_scale"):GetFloat(),
|
||||
weight_map = util.TableToJSON(toolgun),
|
||||
})
|
||||
|
||||
for preset_name, map in pairs(presets.GetTable( "face" )) do
|
||||
local preset = {}
|
||||
for key, weight in pairs(map) do
|
||||
local i = tonumber(key:match("faceposer_flex(%d+)"))
|
||||
if i then
|
||||
local name = ent:GetFlexName(i)
|
||||
if name then
|
||||
preset[name] = tonumber(weight)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
maps[preset_name] = util.TableToJSON({
|
||||
scale = tonumber(map.faceposer_scale),
|
||||
weight_map = util.TableToJSON(preset),
|
||||
})
|
||||
end
|
||||
|
||||
return maps
|
||||
end})
|
||||
:GetSet("FlexWeights", "", {hidden = true})
|
||||
:GetSet("Scale", 1)
|
||||
:GetSet("Additive", false)
|
||||
:EndStorableVars()
|
||||
|
||||
|
||||
-- Make the internal flex names be more presentable, TODO: handle numbers
|
||||
local function PrettifyName( name )
|
||||
name = name:Replace( "_", " " )
|
||||
|
||||
-- Try to split text into words, where words would start with single uppercase character
|
||||
local newParts = {}
|
||||
for id, str in pairs( string.Explode( " ", name ) ) do
|
||||
local wordStart = 1
|
||||
for i = 2, str:len() do
|
||||
local c = str[ i ]
|
||||
if ( c:upper() == c ) then
|
||||
local toAdd = str:sub(wordStart, i - 1)
|
||||
if ( toAdd:upper() == toAdd ) then continue end
|
||||
table.insert( newParts, toAdd )
|
||||
wordStart = i
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
table.insert( newParts, str:sub(wordStart, str:len()))
|
||||
end
|
||||
|
||||
-- Uppercase all first characters
|
||||
for id, str in pairs( newParts ) do
|
||||
if ( str:len() < 2 ) then continue end
|
||||
newParts[ id ] = str:Left( 1 ):upper() .. str:sub( 2 )
|
||||
end
|
||||
|
||||
return table.concat( newParts, " " )
|
||||
end
|
||||
|
||||
|
||||
function PART:GetDynamicProperties()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
if ent.GetFlexNum and ent:GetFlexNum() and ent:GetFlexNum() == 0 then return end
|
||||
|
||||
local tbl = {}
|
||||
|
||||
for i = 0, ent:GetFlexNum() - 1 do
|
||||
local name = ent:GetFlexName(i)
|
||||
|
||||
tbl[name] = {
|
||||
key = name,
|
||||
sort_key = -i,
|
||||
get = function()
|
||||
local weight_map = util.JSONToTable(self:GetFlexWeights()) or {}
|
||||
|
||||
return weight_map[name] or 0
|
||||
end,
|
||||
set = function(val)
|
||||
local weight_map = util.JSONToTable(self:GetFlexWeights()) or {}
|
||||
|
||||
weight_map[name] = tonumber(val) or 0
|
||||
|
||||
self:SetFlexWeights(util.TableToJSON(weight_map))
|
||||
end,
|
||||
udata = {
|
||||
editor_friendly = PrettifyName(name),
|
||||
group = "flexes",
|
||||
editor_sensitivity = 0.1,
|
||||
editor_onchange = function(self, num)
|
||||
local min, max = ent:GetFlexBounds(i)
|
||||
|
||||
return math.Clamp(num, min, max)
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
function PART:SetPreset(json)
|
||||
local preset = util.JSONToTable(json)
|
||||
if preset then
|
||||
self:SetFlexWeights(preset.weight_map)
|
||||
self:SetScale(preset.scale)
|
||||
end
|
||||
self.Preset = ""
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
return "face pose"
|
||||
end
|
||||
|
||||
function PART:GetWeightMap()
|
||||
local data = self:GetFlexWeights()
|
||||
|
||||
if data ~= self.last_data then
|
||||
self.weight_map = util.JSONToTable(data) or {}
|
||||
self.last_data = data
|
||||
end
|
||||
|
||||
return self.weight_map
|
||||
end
|
||||
|
||||
function PART:UpdateFlex()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent:SetFlexScale(self.Scale)
|
||||
ent.pac_touching_flexes = ent.pac_touching_flexes or {}
|
||||
|
||||
for name, weight in pairs(self:GetWeightMap()) do
|
||||
local id = ent:GetFlexIDByName(name)
|
||||
if id then
|
||||
if self.Additive then
|
||||
weight = ent:GetFlexWeight(id) + weight
|
||||
end
|
||||
ent:SetFlexWeight(id, weight)
|
||||
ent.pac_touching_flexes[id] = pac.RealTime + 0.1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsPlayer() then
|
||||
self:UpdateFlex()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
self:UpdateFlex()
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
self:UpdateFlex()
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self:UpdateFlex()
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
self:UpdateFlex()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
66
lua/pac3/core/client/parts/flex.lua
Normal file
66
lua/pac3/core/client/parts/flex.lua
Normal file
@@ -0,0 +1,66 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "flex"
|
||||
|
||||
PART.Icon = 'icon16/emoticon_smile.png'
|
||||
PART.Group = 'entity'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Flex", "", {
|
||||
enums = function(part)
|
||||
local tbl = {}
|
||||
|
||||
for _, v in pairs(pac.GetFlexMap(part:GetOwner())) do
|
||||
tbl[v.name] = v.name
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
})
|
||||
|
||||
BUILDER:GetSet("Weight", 0)
|
||||
BUILDER:GetSet("Additive", false)
|
||||
BUILDER:GetSet("RootOwner", false, { hide_in_editor = true })
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:SetRootOwner(b)
|
||||
self:SetRootOwnerDeprecated(b)
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
return self:GetFlex() ~= "" and self:GetFlex() or "no flex"
|
||||
end
|
||||
|
||||
function PART:GetFlexID()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() or not ent.GetFlexNum or ent:GetFlexNum() == 0 then return end
|
||||
|
||||
local flex_map = pac.GetFlexMap(ent)
|
||||
local flex = flex_map[self.Flex:lower()]
|
||||
|
||||
return flex and flex.i, ent
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
local id, ent = self:GetFlexID()
|
||||
if not id then return end
|
||||
local weight = self.Weight
|
||||
if self.Additive then
|
||||
weight = weight + ent:GetFlexWeight(id)
|
||||
end
|
||||
ent:SetFlexWeight(id, weight)
|
||||
ent.pac_touching_flexes = ent.pac_touching_flexes or {}
|
||||
ent.pac_touching_flexes[id] = pac.RealTime + 0.1
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
80
lua/pac3/core/client/parts/fog.lua
Normal file
80
lua/pac3/core/client/parts/fog.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
--[[
|
||||
| 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 MATERIAL_FOG_NONE = MATERIAL_FOG_NONE
|
||||
local MATERIAL_FOG_LINEAR = MATERIAL_FOG_LINEAR
|
||||
local MATERIAL_FOG_LINEAR_BELOW_FOG_Z = MATERIAL_FOG_LINEAR_BELOW_FOG_Z
|
||||
local render_FogStart = render.FogStart
|
||||
local render_FogEnd = render.FogEnd
|
||||
local render_FogMaxDensity = render.FogMaxDensity
|
||||
local render_SetFogZ = render.SetFogZ
|
||||
local render_FogMode = render.FogMode
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "fog"
|
||||
|
||||
PART.Group = 'modifiers'
|
||||
PART.Icon = 'icon16/weather_clouds.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Start", 0)
|
||||
BUILDER:GetSet("End", 10)
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
--BUILDER:GetSet("AffectChildren", false)
|
||||
BUILDER:GetSet("Height", 0)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local h = pac.ColorToNames(self:GetColor())
|
||||
|
||||
return h .. " fog"
|
||||
end
|
||||
|
||||
function PART:SetColor(v)
|
||||
self.Color = v
|
||||
|
||||
self.clr = {v.r, v.g, v.b}
|
||||
end
|
||||
|
||||
function PART:OnParent(part)
|
||||
if part.AddModifier then
|
||||
part:AddModifier(self)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnUnParent(part)
|
||||
if not part:IsValid() then return end
|
||||
if part.RemoveModifier then
|
||||
part:RemoveModifier(self)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PreOnDraw()
|
||||
render_FogStart(self.Start * 100)
|
||||
render_FogEnd(self.End * 100)
|
||||
render_FogMaxDensity(self.Alpha)
|
||||
if self.clr then render.FogColor(self.clr[1], self.clr[2], self.clr[3]) end
|
||||
|
||||
if self.Height > 0 then
|
||||
render_FogMode(MATERIAL_FOG_LINEAR_BELOW_FOG_Z)
|
||||
render_SetFogZ(self:GetWorldPosition().z + self.Height * 10)
|
||||
else
|
||||
render_FogMode(MATERIAL_FOG_LINEAR)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function PART:PostOnDraw()
|
||||
render.FogMode(MATERIAL_FOG_NONE)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
125
lua/pac3/core/client/parts/gesture.lua
Normal file
125
lua/pac3/core/client/parts/gesture.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "gesture"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = 'entity'
|
||||
PART.Icon = 'icon16/thumb_up.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Loop", false)
|
||||
BUILDER:GetSet("GestureName", "", {editor_panel = "sequence"})
|
||||
BUILDER:GetSet("SlotName", "attackreload", {enums = function(part) return part.ValidGestureSlots end})
|
||||
BUILDER:GetSet("SlotWeight", 1)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
PART.ValidGestureSlots = {
|
||||
attackreload = GESTURE_SLOT_ATTACK_AND_RELOAD,
|
||||
grenade = GESTURE_SLOT_GRENADE,
|
||||
jump = GESTURE_SLOT_JUMP,
|
||||
swim = GESTURE_SLOT_SWIM,
|
||||
flinch = GESTURE_SLOT_FLINCH,
|
||||
vcd = GESTURE_SLOT_VCD,
|
||||
custom = GESTURE_SLOT_CUSTOM
|
||||
}
|
||||
|
||||
function PART:GetSequenceList()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
return ent:GetSequenceList()
|
||||
end
|
||||
|
||||
return {"none"}
|
||||
end
|
||||
|
||||
function PART:GetSlotID()
|
||||
return self.ValidGestureSlots[self.SlotName] or GESTURE_SLOT_CUSTOM
|
||||
end
|
||||
|
||||
function PART:SetLoop(bool)
|
||||
self.Loop = bool
|
||||
|
||||
if not self:IsHidden() then
|
||||
self:OnShow()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetGestureName(name)
|
||||
self.GestureName = name
|
||||
|
||||
local list = name:Split(";")
|
||||
for k,v in next,list do
|
||||
if v:Trim() == "" then list[k] = nil end
|
||||
end
|
||||
|
||||
self.random_gestlist = list
|
||||
|
||||
if not self:IsHidden() then
|
||||
self:OnShow()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSlotName(name)
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent:IsPlayer() then -- to stop gestures getting stuck
|
||||
for _, v in next,self.ValidGestureSlots do
|
||||
ent:AnimResetGestureSlot(v)
|
||||
end
|
||||
end
|
||||
|
||||
self.SlotName = name
|
||||
|
||||
if not self:IsHidden() then
|
||||
self:OnShow()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSlotWeight(num)
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent:IsPlayer() then
|
||||
ent:AnimSetGestureWeight(self:GetSlotID(), num)
|
||||
end
|
||||
|
||||
self.SlotWeight = num
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent:IsPlayer() then -- function is for players only :(
|
||||
local gesture = self.random_gestlist and table.Random(self.random_gestlist) or self.GestureName
|
||||
local slot = self:GetSlotID()
|
||||
|
||||
ent:AnimResetGestureSlot(slot)
|
||||
local act = ent:GetSequenceActivity(ent:LookupSequence(gesture))
|
||||
if act ~= 1 then
|
||||
ent:AnimRestartGesture(slot, act, not self.Loop)
|
||||
end
|
||||
ent:AnimSetGestureWeight(slot, self.SlotWeight or 1)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent:IsPlayer() and self.Loop then
|
||||
ent:AnimResetGestureSlot(self:GetSlotID())
|
||||
end
|
||||
end
|
||||
|
||||
PART.OnRemove = PART.OnHide
|
||||
|
||||
BUILDER:Register()
|
||||
148
lua/pac3/core/client/parts/group.lua
Normal file
148
lua/pac3/core/client/parts/group.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "group"
|
||||
|
||||
PART.Icon = 'icon16/world.png'
|
||||
PART.Description = "right click to add parts"
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Duplicate", false)
|
||||
BUILDER:GetSet("OwnerName", "self")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local init_list = {}
|
||||
local init_index = 0
|
||||
|
||||
pac.AddHook("Think", "group_init", function()
|
||||
if init_index == 0 then return end
|
||||
|
||||
for i = 1, init_index do
|
||||
local self = init_list[i]
|
||||
|
||||
if self:IsValid() and not self:HasParent() and not self.Owner:IsValid() and not self.update_owner_once then
|
||||
self:UpdateOwnerName()
|
||||
end
|
||||
end
|
||||
|
||||
init_list = {}
|
||||
init_index = 0
|
||||
end)
|
||||
|
||||
function PART:Initialize()
|
||||
init_index = init_index + 1
|
||||
init_list[init_index] = self
|
||||
end
|
||||
|
||||
function PART:SetOwner(ent)
|
||||
if self:HasParent() then
|
||||
self.Owner = ent or NULL
|
||||
else
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
pac.UnhookEntityRender(owner, self)
|
||||
end
|
||||
|
||||
self.Owner = ent or NULL
|
||||
owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
if not pac.HookEntityRender(owner, self) then
|
||||
self:ShowFromRendering()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:HideInvalidOwners()
|
||||
local prev_owner = self:GetOwner()
|
||||
|
||||
if not prev_owner:IsValid() then
|
||||
self:SetOwner(NULL)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateOwnerName()
|
||||
-- this is only supported by groups in root
|
||||
self.update_owner_once = true
|
||||
if self:HasParent() then return end
|
||||
|
||||
local ent
|
||||
local prev_owner = self:GetOwner()
|
||||
|
||||
if self.Duplicate then
|
||||
ent = pac.HandleOwnerName(self:GetPlayerOwner(), self.OwnerName, ent, self, function(e) return e.pac_duplicate_attach_uid ~= self.UniqueID end) or NULL
|
||||
|
||||
if ent ~= prev_owner and ent:IsValid() then
|
||||
local tbl = self:ToTable()
|
||||
tbl.self.OwnerName = "self"
|
||||
tbl.self.Duplicate = false
|
||||
pac.SetupENT(ent)
|
||||
local part = ent:AttachPACPart(tbl)
|
||||
part:SetShowInEditor(false)
|
||||
ent:CallOnRemove("pac_remove_outfit_" .. tbl.self.UniqueID, function()
|
||||
ent:RemovePACPart(tbl)
|
||||
end)
|
||||
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
ent:SetPACDrawDistance(0)
|
||||
end
|
||||
|
||||
ent.pac_duplicate_attach_uid = part:GetUniqueID()
|
||||
end
|
||||
else
|
||||
ent = pac.HandleOwnerName(self:GetPlayerOwner(), self.OwnerName, ent, self) or NULL
|
||||
end
|
||||
|
||||
if ent ~= prev_owner then
|
||||
self:SetOwner(ent)
|
||||
end
|
||||
end
|
||||
|
||||
local Base_SetPlayerOwner = PART.SetPlayerOwner
|
||||
|
||||
function PART:SetPlayerOwner(ply)
|
||||
local prev = self.PlayerOwner
|
||||
|
||||
Base_SetPlayerOwner(self, ply)
|
||||
|
||||
if prev:IsValid() then
|
||||
self:UpdateOwnerName()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetOwnerName(name)
|
||||
if name == "" then
|
||||
name = "self"
|
||||
end
|
||||
|
||||
self.OwnerName = name
|
||||
|
||||
if self.Owner:IsValid() then
|
||||
self:UpdateOwnerName()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
return #self:GetChildrenList() .. " children"
|
||||
end
|
||||
|
||||
function PART:OnVehicleChanged(ply, vehicle)
|
||||
if self:HasParent() then return end
|
||||
|
||||
if self.OwnerName == "active vehicle" then
|
||||
self:UpdateOwnerName()
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
94
lua/pac3/core/client/parts/halo.lua
Normal file
94
lua/pac3/core/client/parts/halo.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| 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 Color = Color
|
||||
local Vector = Vector
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "halo"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = {'effects', 'model'}
|
||||
PART.Icon = 'icon16/shading.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("BlurX", 2)
|
||||
BUILDER:GetSet("BlurY", 2)
|
||||
BUILDER:GetSet("Amount", 1)
|
||||
BUILDER:GetSet("Passes", 1)
|
||||
BUILDER:GetSet("SphericalSize", 1)
|
||||
BUILDER:GetSet("Shape", 1)
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Additive", true)
|
||||
BUILDER:GetSet("IgnoreZ", false)
|
||||
BUILDER:GetSet("AffectChildren", false)
|
||||
BUILDER:GetSet("AffectTargetChildren", false)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local h = pac.ColorToNames(self:GetColor())
|
||||
|
||||
return h .. " halo"
|
||||
end
|
||||
|
||||
function PART:SetShape(n)
|
||||
self.Shape = math.Clamp(n, 0, 1)
|
||||
end
|
||||
|
||||
function PART:SetPasses(n)
|
||||
self.Passes = math.min(n, 50)
|
||||
end
|
||||
|
||||
function PART:GetTarget()
|
||||
local parent = self:GetTargetEntity()
|
||||
|
||||
if parent:IsValid() then
|
||||
return parent
|
||||
end
|
||||
|
||||
return self:GetParent()
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
|
||||
local tbl = {}
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
tbl[1] = ent
|
||||
end
|
||||
|
||||
local target = self:GetTarget()
|
||||
|
||||
if self.AffectTargetChildren and target:IsValid() then
|
||||
for _, part in ipairs(target:GetChildrenList()) do
|
||||
local ent = part:GetOwner()
|
||||
if ent:IsValid() and not part:IsHiddenCached() then
|
||||
table.insert(tbl, ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.AffectChildren then
|
||||
for _, part in ipairs(self:GetChildrenList()) do
|
||||
local ent = part:GetOwner()
|
||||
if ent:IsValid() and not part:IsHiddenCached() then
|
||||
table.insert(tbl, ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pac.haloex.Add(tbl, Color(self.Color.r, self.Color.g, self.Color.b), self.BlurX, self.BlurY, self.Passes, self.Additive, self.IgnoreZ, self.Amount, self.SphericalSize, self.Shape)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
175
lua/pac3/core/client/parts/holdtype.lua
Normal file
175
lua/pac3/core/client/parts/holdtype.lua
Normal file
@@ -0,0 +1,175 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "holdtype"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = 'entity'
|
||||
PART.Icon = 'icon16/user_edit.png'
|
||||
|
||||
local act_mods =
|
||||
{
|
||||
"ACT_MP_STAND_IDLE",
|
||||
"ACT_MP_WALK",
|
||||
"ACT_MP_RUN",
|
||||
"ACT_MP_CROUCH_IDLE",
|
||||
"ACT_MP_CROUCHWALK",
|
||||
"ACT_MP_ATTACK_STAND_PRIMARYFIRE",
|
||||
"ACT_MP_ATTACK_CROUCH_PRIMARYFIRE",
|
||||
"ACT_MP_RELOAD_STAND",
|
||||
"ACT_MP_RELOAD_CROUCH",
|
||||
"ACT_MP_JUMP",
|
||||
"ACT_LAND",
|
||||
"ACT_RANGE_ATTACK1",
|
||||
"ACT_MP_SWIM_IDLE",
|
||||
"ACT_MP_SWIM",
|
||||
}
|
||||
|
||||
do
|
||||
local temp = {}
|
||||
|
||||
for _, act in pairs(act_mods) do
|
||||
|
||||
local key = act
|
||||
key = "_" .. key
|
||||
key = key:gsub("ACT_MP_", "")
|
||||
key = key :lower()
|
||||
key = key:gsub("_(.)", function(char)
|
||||
return char:upper()
|
||||
end)
|
||||
|
||||
temp[key] = _G[act]
|
||||
end
|
||||
|
||||
-- ew
|
||||
if temp.Crouchwalk then
|
||||
temp.CrouchWalk = temp.Crouchwalk
|
||||
temp.Crouchwalk = nil
|
||||
end
|
||||
|
||||
act_mods = temp
|
||||
end
|
||||
|
||||
PART.ActMods = act_mods
|
||||
|
||||
local udata = {
|
||||
enums = function(part)
|
||||
if not part.GetSequenceList then return {} end -- ???
|
||||
|
||||
local tbl = {}
|
||||
|
||||
for k, v in pairs(part:GetSequenceList()) do
|
||||
tbl[v] = v
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
}
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
for name in pairs(act_mods) do
|
||||
BUILDER:GetSet(name, "", udata)
|
||||
end
|
||||
|
||||
BUILDER:GetSet("Fallback", "", udata)
|
||||
BUILDER:GetSet("Noclip", "", udata)
|
||||
BUILDER:GetSet("Air", "", udata)
|
||||
BUILDER:GetSet("Sitting", "", udata)
|
||||
BUILDER:GetSet("AlternativeRate", false)
|
||||
BUILDER:GetSet("Override", false)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
for name in pairs(act_mods) do
|
||||
PART["Set" .. name] = function(self, str)
|
||||
self[name] = str
|
||||
|
||||
if not self:IsHidden() then
|
||||
self:UpdateActTable()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetFallback(str)
|
||||
self.Fallback = str
|
||||
if not self:IsHidden() then
|
||||
self:UpdateActTable()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateActTable()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent.pac_holdtype_alternative_animation_rate = self.AlternativeRate
|
||||
|
||||
ent.pac_holdtypes = ent.pac_holdtypes or {}
|
||||
|
||||
if self.Override then
|
||||
table.Empty(ent.pac_holdtypes)
|
||||
end
|
||||
|
||||
ent.pac_holdtypes[self] = ent.pac_holdtypes[self] or {}
|
||||
|
||||
local acts = ent.pac_holdtypes[self]
|
||||
|
||||
for name, act in pairs(act_mods) do
|
||||
acts[act] = ent:GetSequenceActivity(ent:LookupSequence(self[name]))
|
||||
end
|
||||
|
||||
-- custom acts
|
||||
acts.fallback = ent:GetSequenceActivity(ent:LookupSequence(self.Fallback))
|
||||
acts.noclip = ent:GetSequenceActivity(ent:LookupSequence(self.Noclip))
|
||||
acts.air = ent:GetSequenceActivity(ent:LookupSequence(self.Air))
|
||||
acts.sitting = ent:GetSequenceActivity(ent:LookupSequence(self.Sitting))
|
||||
|
||||
acts.part = self
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if (ent:GetModel() ~= self.last_model or ent.pac_holdtypes ~= self.last_pac_holdtypes) then
|
||||
self:UpdateActTable()
|
||||
self.last_model = ent:GetModel()
|
||||
self.last_pac_holdtypes = ent.pac_holdtypes
|
||||
end
|
||||
end
|
||||
|
||||
function PART:GetSequenceList()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
return ent:GetSequenceList()
|
||||
end
|
||||
|
||||
return {"none"}
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if ent.pac_holdtypes then
|
||||
ent.pac_holdtypes[self] = nil
|
||||
end
|
||||
|
||||
ent.pac_holdtype_alternative_animation_rate = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self:UpdateActTable()
|
||||
end
|
||||
|
||||
|
||||
BUILDER:Register()
|
||||
21
lua/pac3/core/client/parts/info.lua
Normal file
21
lua/pac3/core/client/parts/info.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "info"
|
||||
|
||||
PART.Group = ''
|
||||
PART.Icon = 'icon16/help.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("SpawnEntity", "")
|
||||
BUILDER:GetSet("UserData", "")
|
||||
BUILDER:EndStorableVars()
|
||||
202
lua/pac3/core/client/parts/jiggle.lua
Normal file
202
lua/pac3/core/client/parts/jiggle.lua
Normal file
@@ -0,0 +1,202 @@
|
||||
--[[
|
||||
| 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 FrameTime = FrameTime
|
||||
local util_QuickTrace = util.QuickTrace
|
||||
local VectorRand = VectorRand
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
local physenv_GetGravity = physenv.GetGravity
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "jiggle"
|
||||
PART.Group = 'model'
|
||||
PART.Icon = 'icon16/chart_line.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Strain", 0.5, {editor_onchange = function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Clamp(num, 0, 1) * 0.999
|
||||
end})
|
||||
BUILDER:GetSet("Speed", 1)
|
||||
BUILDER:GetSet("ConstantVelocity", Vector(0, 0, 0))
|
||||
BUILDER:GetSet("LocalVelocity", true)
|
||||
BUILDER:GetSet("JiggleAngle", true)
|
||||
BUILDER:GetSet("JigglePosition", true)
|
||||
|
||||
BUILDER:GetSet("ConstrainPitch", false)
|
||||
BUILDER:GetSet("ConstrainYaw", false)
|
||||
BUILDER:GetSet("ConstrainRoll", false)
|
||||
|
||||
BUILDER:GetSet("ConstrainX", false)
|
||||
BUILDER:GetSet("ConstrainY", false)
|
||||
BUILDER:GetSet("ConstrainZ", false)
|
||||
|
||||
BUILDER:GetSet("ConstrainSphere", 0)
|
||||
BUILDER:GetSet("StopRadius", 0)
|
||||
BUILDER:GetSet("Ground", false)
|
||||
BUILDER:GetSet("ResetOnHide", false)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local math_AngleDifference = math.AngleDifference
|
||||
|
||||
function PART:Reset()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
self.pos = pos or Vector()
|
||||
self.vel = Vector()
|
||||
|
||||
self.ang = ang or Angle()
|
||||
self.angvel = Angle()
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self.pos = Vector()
|
||||
self.vel = Vector()
|
||||
|
||||
self.ang = Angle()
|
||||
self.angvel = Angle()
|
||||
|
||||
self.first_time_reset = true
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
if self.ResetOnHide then
|
||||
self:Reset()
|
||||
end
|
||||
end
|
||||
|
||||
local inf, ninf = math.huge, -math.huge
|
||||
|
||||
local function check_num(num)
|
||||
if num ~= inf and num ~= ninf and (num >= 0 or num <= 0) then
|
||||
return num
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
if self.first_time_reset then
|
||||
self:Reset()
|
||||
self.first_time_reset = false
|
||||
end
|
||||
|
||||
local delta = FrameTime()
|
||||
local speed = self.Speed * delta
|
||||
|
||||
self.vel = self.vel or VectorRand()
|
||||
self.pos = self.pos or pos * 1
|
||||
|
||||
if self.StopRadius ~= 0 and self.pos and self.pos:Distance(pos) < self.StopRadius then
|
||||
self.vel = Vector()
|
||||
return
|
||||
end
|
||||
|
||||
if self.JigglePosition then
|
||||
if not self.ConstrainX then
|
||||
self.vel.x = self.vel.x + (pos.x - self.pos.x)
|
||||
|
||||
if self.LocalVelocity then
|
||||
self.vel = self.vel + ang:Right() * self.ConstantVelocity.x
|
||||
else
|
||||
self.vel.x = self.vel.x + self.ConstantVelocity.x
|
||||
end
|
||||
|
||||
self.pos.x = self.pos.x + (self.vel.x * (self.Invert and -speed or speed))
|
||||
self.vel.x = self.vel.x * self.Strain
|
||||
else
|
||||
self.pos.x = pos.x
|
||||
end
|
||||
|
||||
if not self.ConstrainY then
|
||||
self.vel.y = self.vel.y + (pos.y - self.pos.y)
|
||||
|
||||
if self.LocalVelocity then
|
||||
self.vel = self.vel + ang:Forward() * self.ConstantVelocity.y
|
||||
else
|
||||
self.vel.y = self.vel.y + self.ConstantVelocity.y
|
||||
end
|
||||
|
||||
self.pos.y = self.pos.y + (self.vel.y * speed)
|
||||
self.vel.y = self.vel.y * self.Strain
|
||||
else
|
||||
self.pos.y = pos.y
|
||||
end
|
||||
|
||||
if not self.ConstrainZ then
|
||||
self.vel.z = self.vel.z + (pos.z - self.pos.z)
|
||||
|
||||
if self.LocalVelocity then
|
||||
self.vel = self.vel + ang:Up() * self.ConstantVelocity.z
|
||||
else
|
||||
self.vel.z = self.vel.z + self.ConstantVelocity.z
|
||||
end
|
||||
|
||||
self.pos.z = self.pos.z + (self.vel.z * speed)
|
||||
self.vel.z = self.vel.z * self.Strain
|
||||
else
|
||||
self.pos.z = pos.z
|
||||
end
|
||||
|
||||
if self.Ground then
|
||||
self.pos.z = util_QuickTrace(pos, physenv_GetGravity() * 100).HitPos.z
|
||||
end
|
||||
else
|
||||
self.pos = pos
|
||||
end
|
||||
|
||||
if self.ConstrainSphere > 0 then
|
||||
local len = math.min(self.pos:Distance(pos), self.ConstrainSphere)
|
||||
|
||||
self.pos = pos + (self.pos - pos):GetNormalized() * len
|
||||
end
|
||||
|
||||
if self.JiggleAngle then
|
||||
self.angvel = self.angvel or ang * 1
|
||||
self.ang = self.ang or ang * 1
|
||||
|
||||
if not self.ConstrainPitch then
|
||||
self.angvel.p = self.angvel.p + math_AngleDifference(ang.p, self.ang.p)
|
||||
self.ang.p = math_AngleDifference(self.ang.p, self.angvel.p * -speed)
|
||||
self.angvel.p = self.angvel.p * self.Strain
|
||||
end
|
||||
|
||||
if not self.ConstrainYaw then
|
||||
self.angvel.y = self.angvel.y + math_AngleDifference(ang.y, self.ang.y)
|
||||
self.ang.y = math_AngleDifference(self.ang.y, self.angvel.y * -speed)
|
||||
self.angvel.y = self.angvel.y * self.Strain
|
||||
end
|
||||
|
||||
if not self.ConstrainRoll then
|
||||
self.angvel.r = self.angvel.r + math_AngleDifference(ang.r, self.ang.r)
|
||||
self.ang.r = math_AngleDifference(self.ang.r, self.angvel.r * -speed)
|
||||
self.angvel.r = self.angvel.r * self.Strain
|
||||
end
|
||||
else
|
||||
self.ang = ang
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
self.pos.x = check_num(self.pos.x)
|
||||
self.pos.y = check_num(self.pos.y)
|
||||
self.pos.z = check_num(self.pos.z)
|
||||
|
||||
self.ang.p = check_num(self.ang.p)
|
||||
self.ang.y = check_num(self.ang.y)
|
||||
self.ang.r = check_num(self.ang.r)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
145
lua/pac3/core/client/parts/legacy/bodygroup.lua
Normal file
145
lua/pac3/core/client/parts/legacy/bodygroup.lua
Normal file
@@ -0,0 +1,145 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "bodygroup"
|
||||
PART.Group = "legacy"
|
||||
PART.Icon = 'icon16/user.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("BodyGroupName", "", {
|
||||
enums = function()
|
||||
return pace.current_part:GetBodyGroupNameList()
|
||||
end
|
||||
})
|
||||
BUILDER:GetSet("ModelIndex", 0)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:OnShow()
|
||||
self:SetBodyGroupName(self:GetBodyGroupName())
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
return self.BodyGroupName ~= "" and self.BodyGroupName or "no bodygroup"
|
||||
end
|
||||
|
||||
function PART:SetBodyGroupName(str)
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() and not self.markedFailed and self.bodygroup_index and self.oldBodygroup then
|
||||
owner:SetBodygroup(self.bodygroup_index, self.oldBodygroup)
|
||||
|
||||
if owner:IsPlayer() then
|
||||
owner.pac_bodygroups_torender = owner.pac_bodygroups_torender or {}
|
||||
owner.pac_bodygroups_torender[self.bodygroup_index] = self.oldBodygroup
|
||||
end
|
||||
|
||||
self.oldBodygroup = nil
|
||||
end
|
||||
|
||||
self.BodyGroupName = str
|
||||
self.markedFailed = false
|
||||
self:UpdateBodygroupData()
|
||||
end
|
||||
|
||||
function PART:SetModelIndex(i)
|
||||
self.ModelIndex = math.floor(tonumber(i) or 0)
|
||||
self.markedFailed = false
|
||||
self:UpdateBodygroupData()
|
||||
end
|
||||
|
||||
function PART:UpdateBodygroupData()
|
||||
self.bodygroup_index = nil
|
||||
self.minIndex = 0
|
||||
self.maxIndex = 0
|
||||
self:SetError()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not IsValid(ent) or not ent:GetBodyGroups() then return end
|
||||
local fName = self.BodyGroupName:lower():Trim()
|
||||
|
||||
if fName == '' then
|
||||
return
|
||||
end
|
||||
|
||||
for i, info in ipairs(ent:GetBodyGroups()) do
|
||||
if info.name:lower():Trim() == fName then
|
||||
self.bodygroup_index = info.id
|
||||
self.maxIndex = info.num - 1
|
||||
self.markedFailed = false
|
||||
self.oldBodygroup = ent:GetBodygroup(info.id)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not self.markedFailed then
|
||||
pac.Message(self, ' - Unable to find bodygroup ' .. fName .. ' on ', ent)
|
||||
self:SetError("Unable to find bodygroup " .. fName .. " on " .. tostring(ent))
|
||||
self.markedFailed = true
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
if self.markedFailed then return end
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if not owner:IsValid() then return end
|
||||
if not self.bodygroup_index then
|
||||
self:UpdateBodygroupData()
|
||||
return
|
||||
end
|
||||
|
||||
owner:SetBodygroup(self.bodygroup_index, self.ModelIndex)
|
||||
|
||||
if owner:IsPlayer() then
|
||||
owner.pac_bodygroups_torender = owner.pac_bodygroups_torender or {}
|
||||
owner.pac_bodygroups_torender[self.bodygroup_index] = self.ModelIndex
|
||||
end
|
||||
end
|
||||
|
||||
-- for the editor
|
||||
|
||||
function PART:GetModelIndexList()
|
||||
local out = {}
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
for _, info in pairs(ent:GetBodyGroups()) do
|
||||
if info.id == self.bodygroup_info.id then
|
||||
for _, model in pairs(info.submodels) do
|
||||
table.insert(out, model)
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function PART:GetBodyGroupNameList()
|
||||
local out = {}
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
for _, info in pairs(ent:GetBodyGroups()) do
|
||||
out[info.name] = info.name
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
297
lua/pac3/core/client/parts/legacy/bone.lua
Normal file
297
lua/pac3/core/client/parts/legacy/bone.lua
Normal file
@@ -0,0 +1,297 @@
|
||||
--[[
|
||||
| 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 NULL = NULL
|
||||
local pairs = pairs
|
||||
|
||||
for _, v in pairs(ents.GetAll()) do
|
||||
v.pac_bone_setup_data = nil
|
||||
end
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "legacy bone"
|
||||
PART.ClassName = "bone"
|
||||
PART.Group = "legacy"
|
||||
PART.is_bone_part = true
|
||||
|
||||
PART.Icon = 'icon16/connect.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("Jiggle", false)
|
||||
BUILDER:GetSet("ScaleChildren", false)
|
||||
BUILDER:GetSet("AlternativeBones", false)
|
||||
BUILDER:GetSet("MoveChildrenToOrigin", false)
|
||||
BUILDER:GetSet("FollowAnglesOnly", false)
|
||||
--BUILDER:GetSet("HideMesh", false)
|
||||
--BUILDER:GetSet("InvertHideMesh", false)
|
||||
BUILDER:GetSetPart("FollowPart")
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:PropertyOrder("AimPartName")
|
||||
BUILDER:PropertyOrder("Bone")
|
||||
BUILDER:PropertyOrder("Position")
|
||||
BUILDER:PropertyOrder("Angles")
|
||||
BUILDER:PropertyOrder("EyeAngles")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Scale", Vector(1,1,1), {editor_sensitivity = 0.25})
|
||||
BUILDER:PropertyOrder("PositionOffset")
|
||||
BUILDER:PropertyOrder("AngleOffset")
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
|
||||
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
BUILDER:RemoveProperty("Translucent")
|
||||
BUILDER:RemoveProperty("IgnoreZ")
|
||||
BUILDER:RemoveProperty("BlendMode")
|
||||
BUILDER:RemoveProperty("NoTextureFiltering")
|
||||
|
||||
local BaseClass_GetOwner = PART.GetOwner
|
||||
|
||||
function PART:GetNiceName()
|
||||
return self:GetBone()
|
||||
end
|
||||
|
||||
PART.ThinkTime = 0
|
||||
|
||||
function PART:OnShow()
|
||||
self:SetBone(self:GetBone())
|
||||
end
|
||||
PART.OnParent = PART.OnShow
|
||||
|
||||
function PART:OnThink()
|
||||
-- this is to setup the cached values
|
||||
if not self.first_getbpos and self:GetOwner():IsValid() then
|
||||
self:GetBonePosition()
|
||||
self.first_getbpos = true
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
owner.pac_bone_setup_data = owner.pac_bone_setup_data or {}
|
||||
owner.pac_bone_setup_data[self.UniqueID] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:GetBonePosition()
|
||||
local owner = self:GetOwner()
|
||||
local pos, ang
|
||||
|
||||
pos, ang = pac.GetBonePosAng(owner, self.Bone, true)
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
local function manpos(ent, id, pos, part)
|
||||
if part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].pos = part.Position + part.PositionOffset
|
||||
else
|
||||
ent:ManipulateBonePosition(id, ent:GetManipulateBonePosition(id) + pos)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local function manang(ent, id, ang, part)
|
||||
if part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].ang = part.Angles + part.AngleOffset
|
||||
else
|
||||
ent:ManipulateBoneAngles(id, ent:GetManipulateBoneAngles(id) + ang)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local inf_scale = Vector(math.huge, math.huge, math.huge)
|
||||
local inf_scale_tempcrashfix = Vector(1,1,1)*0.001
|
||||
|
||||
local function manscale(ent, id, scale, part)
|
||||
if part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].scale = scale
|
||||
else
|
||||
ent:ManipulateBoneScale(id, ent:GetManipulateBoneScale(id) * scale)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local function scale_children(owner, id, scale, origin, ownerScale)
|
||||
local count = owner:GetBoneCount()
|
||||
ownerScale = ownerScale or owner.pac3_Scale or 1
|
||||
|
||||
if count == 0 or count < id then return end
|
||||
|
||||
for i = 0, count - 1 do
|
||||
if owner:GetBoneParent(i) ~= id then goto CONTINUE end
|
||||
|
||||
local mat = owner:GetBoneMatrix(i)
|
||||
|
||||
if mat then
|
||||
if origin then
|
||||
mat:SetTranslation(origin)
|
||||
end
|
||||
|
||||
mat:Scale(mat:GetScale() * scale / ownerScale)
|
||||
owner:SetBoneMatrix(i, mat)
|
||||
end
|
||||
|
||||
scale_children(owner, i, scale, origin, ownerScale)
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
local in_build = false
|
||||
function pac.build_bone_callback(ent)
|
||||
if in_build then return end
|
||||
|
||||
in_build = true
|
||||
if ent.pac_matrixhack then
|
||||
pac.LegacyScale(ent)
|
||||
end
|
||||
|
||||
if ent.pac_bone_setup_data then
|
||||
for uid, data in pairs(ent.pac_bone_setup_data) do
|
||||
local part = data.part or NULL
|
||||
|
||||
if part:IsValid() then
|
||||
local mat = ent:GetBoneMatrix(data.bone)
|
||||
if mat then
|
||||
if part.FollowPart:IsValid() and part.FollowPart.GetWorldAngles and part.FollowPart.GetWorldPosition then
|
||||
if part.FollowAnglesOnly then
|
||||
local pos = mat:GetTranslation()
|
||||
mat:SetAngles(part.Angles + part.AngleOffset + part.FollowPart:GetWorldAngles())
|
||||
mat:SetTranslation(pos)
|
||||
else
|
||||
mat:SetAngles(part.Angles + part.AngleOffset + part.FollowPart:GetWorldAngles())
|
||||
mat:SetTranslation(part.Position + part.PositionOffset + part.FollowPart:GetWorldPosition())
|
||||
end
|
||||
else
|
||||
if data.pos then
|
||||
mat:Translate(data.pos)
|
||||
end
|
||||
|
||||
if data.ang then
|
||||
mat:Rotate(data.ang)
|
||||
end
|
||||
end
|
||||
|
||||
if data.scale then
|
||||
mat:Scale(mat:GetScale() * data.scale)
|
||||
end
|
||||
|
||||
if part.ScaleChildren then
|
||||
local scale = part.Scale * part.Size
|
||||
scale_children(ent, data.bone, scale, data.origin)
|
||||
end
|
||||
|
||||
ent:SetBoneMatrix(data.bone, mat)
|
||||
end
|
||||
else
|
||||
ent.pac_bone_setup_data[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
in_build = false
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
local index = self:GetModelBoneIndex()
|
||||
|
||||
if not index then
|
||||
index = 0
|
||||
end
|
||||
|
||||
owner.pac_bone_setup_data = owner.pac_bone_setup_data or {}
|
||||
|
||||
if self.AlternativeBones or self.ScaleChildren or self.FollowPart:IsValid() then
|
||||
owner.pac_bone_setup_data[self.UniqueID] = owner.pac_bone_setup_data[self.UniqueID] or {}
|
||||
owner.pac_bone_setup_data[self.UniqueID].bone = index
|
||||
owner.pac_bone_setup_data[self.UniqueID].part = self
|
||||
else
|
||||
owner.pac_bone_setup_data[self.UniqueID] = nil
|
||||
end
|
||||
|
||||
local ang = self:CalcAngles(self.Angles) or self.Angles
|
||||
|
||||
if not owner.pac_follow_bones_function then
|
||||
owner.pac_follow_bones_function = pac.build_bone_callback
|
||||
local id
|
||||
id = owner:AddCallback("BuildBonePositions", function(ent)
|
||||
if not self:IsValid() then
|
||||
owner:RemoveCallback("BuildBonePositions", id)
|
||||
return
|
||||
end
|
||||
pac.build_bone_callback(ent)
|
||||
end)
|
||||
end
|
||||
|
||||
if not self.FollowPart:IsValid() then
|
||||
if self.EyeAngles or self.AimPart:IsValid() then
|
||||
ang.r = ang.y
|
||||
ang.y = -ang.p
|
||||
end
|
||||
|
||||
manpos(owner, index, self.Position + self.PositionOffset, self)
|
||||
manang(owner, index, ang + self.AngleOffset, self)
|
||||
end
|
||||
|
||||
if owner.pac_bone_setup_data[self.UniqueID] then
|
||||
if self.MoveChildrenToOrigin then
|
||||
owner.pac_bone_setup_data[self.UniqueID].origin = self:GetBonePosition()
|
||||
else
|
||||
owner.pac_bone_setup_data[self.UniqueID].origin = nil
|
||||
end
|
||||
end
|
||||
|
||||
owner:ManipulateBoneJiggle(index, isnumber(self.Jiggle) and self.Jiggle or (self.Jiggle and 1 or 0)) -- afaik anything but 1 is not doing anything at all
|
||||
|
||||
local scale
|
||||
|
||||
if self.HideMesh then
|
||||
scale = inf_scale
|
||||
|
||||
owner.pac_inf_scale = true
|
||||
|
||||
if self.InvertHideMesh then
|
||||
local count = owner:GetBoneCount()
|
||||
|
||||
for i = 0, count - 1 do
|
||||
if i ~= index then
|
||||
manscale(owner, i, inf_scale, self)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
else
|
||||
|
||||
owner.pac_inf_scale = false
|
||||
|
||||
scale = self.Scale * self.Size
|
||||
end
|
||||
|
||||
manscale(owner, index, scale, self)
|
||||
|
||||
owner.needs_setupbones_from_legacy_bone_parts = true
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
297
lua/pac3/core/client/parts/legacy/bone2.lua
Normal file
297
lua/pac3/core/client/parts/legacy/bone2.lua
Normal file
@@ -0,0 +1,297 @@
|
||||
--[[
|
||||
| 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 NULL = NULL
|
||||
local pairs = pairs
|
||||
|
||||
for _, v in pairs(ents.GetAll()) do
|
||||
v.pac_bone_setup_data = nil
|
||||
end
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "legacy experimental bone"
|
||||
PART.ClassName = "bone2"
|
||||
PART.Group = "legacy"
|
||||
PART.Icon = 'icon16/connect.png'
|
||||
PART.is_bone_part = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("Jiggle", false)
|
||||
BUILDER:GetSet("ScaleChildren", false)
|
||||
BUILDER:GetSet("AlternativeBones", false)
|
||||
BUILDER:GetSet("MoveChildrenToOrigin", false)
|
||||
BUILDER:GetSet("FollowAnglesOnly", false)
|
||||
BUILDER:GetSet("HideMesh", false)
|
||||
BUILDER:GetSet("InvertHideMesh", false)
|
||||
BUILDER:GetSetPart("FollowPart")
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:PropertyOrder("AimPartName")
|
||||
BUILDER:PropertyOrder("Bone")
|
||||
BUILDER:PropertyOrder("Position")
|
||||
BUILDER:PropertyOrder("Angles")
|
||||
BUILDER:PropertyOrder("EyeAngles")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Scale", Vector(1,1,1), {editor_sensitivity = 0.25})
|
||||
BUILDER:PropertyOrder("PositionOffset")
|
||||
BUILDER:PropertyOrder("AngleOffset")
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
|
||||
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
BUILDER:RemoveProperty("Translucent")
|
||||
BUILDER:RemoveProperty("IgnoreZ")
|
||||
BUILDER:RemoveProperty("BlendMode")
|
||||
BUILDER:RemoveProperty("NoTextureFiltering")
|
||||
|
||||
function PART:GetNiceName()
|
||||
return self:GetBone()
|
||||
end
|
||||
|
||||
PART.ThinkTime = 0
|
||||
|
||||
function PART:OnShow()
|
||||
self:SetBone(self:GetBone())
|
||||
end
|
||||
|
||||
PART.OnParent = PART.OnShow
|
||||
|
||||
function PART:OnThink()
|
||||
-- this is to setup the cached values
|
||||
if not self.first_getbpos and self:GetOwner():IsValid() then
|
||||
self:GetBonePosition()
|
||||
self.first_getbpos = true
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
owner.pac_bone_setup_data = owner.pac_bone_setup_data or {}
|
||||
owner.pac_bone_setup_data[self.UniqueID] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:GetBonePosition()
|
||||
local owner = self:GetOwner()
|
||||
return pac.GetBonePosAng(owner, self.Bone, true)
|
||||
end
|
||||
|
||||
local function manpos(ent, id, pos, part)
|
||||
if part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].pos = part.Position + part.PositionOffset
|
||||
else
|
||||
ent:ManipulateBonePosition(id, ent:GetManipulateBonePosition(id) + pos)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local function manang(ent, id, ang, part)
|
||||
if part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].ang = part.Angles + part.AngleOffset
|
||||
else
|
||||
ent:ManipulateBoneAngles(id, ent:GetManipulateBoneAngles(id) + ang)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local inf_scale = Vector(math.huge, math.huge, math.huge)
|
||||
|
||||
local function manscale(ent, id, scale, part)
|
||||
if part and part.AlternativeBones then
|
||||
ent.pac_bone_setup_data[part.UniqueID].scale = scale
|
||||
else
|
||||
ent:ManipulateBoneScale(id, ent:GetManipulateBoneScale(id) * scale)
|
||||
part.modified_bones = true
|
||||
end
|
||||
end
|
||||
|
||||
local function scale_children(owner, id, scale, origin, ownerScale)
|
||||
local count = owner:GetBoneCount()
|
||||
ownerScale = ownerScale or owner.pac3_Scale or 1
|
||||
|
||||
if count == 0 or count < id then return end
|
||||
|
||||
for i = 0, count - 1 do
|
||||
if owner:GetBoneParent(i) ~= id then goto CONTINUE end
|
||||
|
||||
local mat = owner:GetBoneMatrix(i)
|
||||
|
||||
if mat then
|
||||
if origin then
|
||||
mat:SetTranslation(origin)
|
||||
end
|
||||
|
||||
mat:Scale(mat:GetScale() * scale / ownerScale)
|
||||
owner:SetBoneMatrix(i, mat)
|
||||
end
|
||||
|
||||
scale_children(owner, i, scale, origin, ownerScale)
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
function pac.build_bone_callback(ent)
|
||||
if ent.pac_matrixhack then
|
||||
pac.LegacyScale(ent)
|
||||
end
|
||||
|
||||
if ent.pac_bone_setup_data then
|
||||
for uid, data in pairs(ent.pac_bone_setup_data) do
|
||||
local part = data.part or NULL
|
||||
|
||||
if part:IsValid() then
|
||||
local mat = ent:GetBoneMatrix(data.bone)
|
||||
if mat then
|
||||
if part.FollowPart:IsValid() and part.FollowPart.GetWorldPosition then
|
||||
|
||||
mat:SetAngles(part.FollowPart:GetWorldAngles())
|
||||
|
||||
if not part.FollowAnglesOnly then
|
||||
mat:SetTranslation(part.FollowPart:GetWorldPosition())
|
||||
end
|
||||
|
||||
else
|
||||
if data.pos then
|
||||
mat:Translate(data.pos)
|
||||
end
|
||||
|
||||
if data.ang then
|
||||
mat:Rotate(data.ang)
|
||||
end
|
||||
end
|
||||
|
||||
if data.scale then
|
||||
mat:Scale(mat:GetScale() * data.scale)
|
||||
end
|
||||
|
||||
if part.ScaleChildren then
|
||||
local scale = part.Scale * part.Size
|
||||
scale_children(ent, data.bone, scale, data.origin)
|
||||
end
|
||||
|
||||
ent:SetBoneMatrix(data.bone, mat)
|
||||
end
|
||||
else
|
||||
ent.pac_bone_setup_data[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
local index = self:GetModelBoneIndex()
|
||||
|
||||
if not index then return end
|
||||
|
||||
owner.pac_bone_setup_data = owner.pac_bone_setup_data or {}
|
||||
|
||||
if self.AlternativeBones or self.ScaleChildren or self.FollowPart:IsValid() then
|
||||
owner.pac_bone_setup_data[self.UniqueID] = owner.pac_bone_setup_data[self.UniqueID] or {}
|
||||
owner.pac_bone_setup_data[self.UniqueID].bone = index
|
||||
owner.pac_bone_setup_data[self.UniqueID].part = self
|
||||
else
|
||||
owner.pac_bone_setup_data[self.UniqueID] = nil
|
||||
end
|
||||
|
||||
local ang = self:CalcAngles(self.Angles) or self.Angles
|
||||
|
||||
if not owner.pac_follow_bones_function then
|
||||
owner.pac_follow_bones_function = pac.build_bone_callback
|
||||
local id
|
||||
id = owner:AddCallback("BuildBonePositions", function(ent)
|
||||
if pac and pac.build_bone_callback then
|
||||
pac.build_bone_callback(ent)
|
||||
else
|
||||
owner:RemoveCallback("BuildBonePositions", id)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if not self.FollowPart:IsValid() then
|
||||
if self.EyeAngles or self.AimPart:IsValid() then
|
||||
ang.r = ang.y
|
||||
ang.y = -ang.p
|
||||
end
|
||||
|
||||
local pos2, ang2 = self.Position + self.PositionOffset, ang + self.AngleOffset
|
||||
|
||||
local parent = self:GetParent()
|
||||
|
||||
if parent and parent:IsValid() and parent.ClassName == 'jiggle' then
|
||||
local pos3, ang3 = parent.Position, parent.Angles
|
||||
|
||||
if parent.pos then
|
||||
pos2 = pos2 + parent.pos - pos3
|
||||
end
|
||||
|
||||
if parent.ang then
|
||||
ang2 = ang2 + parent.ang - ang3
|
||||
end
|
||||
end
|
||||
|
||||
manpos(owner, index, pos2, self)
|
||||
manang(owner, index, ang2, self)
|
||||
end
|
||||
|
||||
if owner.pac_bone_setup_data[self.UniqueID] then
|
||||
if self.MoveChildrenToOrigin then
|
||||
owner.pac_bone_setup_data[self.UniqueID].origin = self:GetBonePosition()
|
||||
else
|
||||
owner.pac_bone_setup_data[self.UniqueID].origin = nil
|
||||
end
|
||||
end
|
||||
|
||||
owner:ManipulateBoneJiggle(index, isnumber(self.Jiggle) and self.Jiggle or (self.Jiggle and 1 or 0)) -- afaik anything but 1 is not doing anything at all
|
||||
|
||||
local scale
|
||||
|
||||
if self.HideMesh then
|
||||
scale = inf_scale
|
||||
owner.pac_inf_scale = true
|
||||
|
||||
if self.InvertHideMesh then
|
||||
local count = owner:GetBoneCount()
|
||||
|
||||
for i = 0, count - 1 do
|
||||
if i ~= index then
|
||||
manscale(owner, i, inf_scale, self)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
else
|
||||
owner.pac_inf_scale = false
|
||||
|
||||
scale = self.Scale * self.Size
|
||||
end
|
||||
|
||||
manscale(owner, index, scale, self)
|
||||
|
||||
owner.needs_setupbones_from_legacy_bone_parts = true
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
77
lua/pac3/core/client/parts/legacy/clip.lua
Normal file
77
lua/pac3/core/client/parts/legacy/clip.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
| 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 render_EnableClipping = render.EnableClipping
|
||||
local render_PushCustomClipPlane = render.PushCustomClipPlane
|
||||
local LocalToWorld = LocalToWorld
|
||||
local IsEntity = IsEntity
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "legacy clip"
|
||||
PART.ClassName = "clip"
|
||||
PART.Group = "legacy"
|
||||
PART.Icon = 'icon16/cut.png'
|
||||
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
|
||||
BUILDER:RemoveProperty("IgnoreZ")
|
||||
BUILDER:RemoveProperty("BlendMode")
|
||||
BUILDER:RemoveProperty("NoTextureFiltering")
|
||||
|
||||
|
||||
function PART:OnParent(part)
|
||||
if not part.AddModifier then return end
|
||||
|
||||
part:AddModifier(self)
|
||||
|
||||
-- this is only really for halos..
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
function ent.pacDrawModel(ent)
|
||||
self:PreOnDraw()
|
||||
ent:DrawModel()
|
||||
self:PostOnDraw()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnUnParent(part)
|
||||
if not part:IsValid() then return end
|
||||
if not part.RemoveModifier then return end
|
||||
part:RemoveModifier(self)
|
||||
end
|
||||
|
||||
do
|
||||
local bclip
|
||||
|
||||
function PART:PreOnDraw()
|
||||
bclip = render_EnableClipping(true)
|
||||
|
||||
local pos, ang = LocalToWorld(self.Position + self.PositionOffset, self:CalcAngles(self.Angles + self.AngleOffset), self:GetBonePosition())
|
||||
local normal = ang:Forward()
|
||||
|
||||
render_PushCustomClipPlane(normal, normal:Dot(pos + normal))
|
||||
end
|
||||
|
||||
local render_PopCustomClipPlane = render.PopCustomClipPlane
|
||||
|
||||
function PART:PostOnDraw()
|
||||
render_PopCustomClipPlane()
|
||||
|
||||
render_EnableClipping(bclip)
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
628
lua/pac3/core/client/parts/legacy/entity.lua
Normal file
628
lua/pac3/core/client/parts/legacy/entity.lua
Normal file
@@ -0,0 +1,628 @@
|
||||
--[[
|
||||
| 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 render_CullMode = render.CullMode
|
||||
local render_SuppressEngineLighting = render.SuppressEngineLighting
|
||||
local render_SetBlend = render.SetBlend
|
||||
local render_SetColorModulation = render.SetColorModulation
|
||||
local render_MaterialOverride = render.MaterialOverride
|
||||
local game_SinglePlayer = game.SinglePlayer
|
||||
local Angle = Angle
|
||||
local Vector = Vector
|
||||
local NULL = NULL
|
||||
local Color = Color
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "legacy entity"
|
||||
PART.ClassName = "entity"
|
||||
PART.Group = 'legacy'
|
||||
PART.Icon = 'icon16/brick.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:GetSet("Model", "")
|
||||
BUILDER:GetSet("Material", "")
|
||||
BUILDER:GetSet("HideEntity", false)
|
||||
BUILDER:GetSet("DrawWeapon", true)
|
||||
BUILDER:GetSet("MuteSounds", false)
|
||||
BUILDER:GetSet("AllowOggWhenMuted", false)
|
||||
BUILDER:GetSet("HideBullets", false)
|
||||
BUILDER:GetSet("DrawPlayerOnDeath", false)
|
||||
BUILDER:GetSet("HidePhysgunBeam", false)
|
||||
BUILDER:GetSet("UseLegacyScale", false)
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Brightness", 1)
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
BUILDER:GetSet("Fullbright", false)
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
BUILDER:PropertyOrder("Translucent")
|
||||
BUILDER:GetSet("Invert", false)
|
||||
BUILDER:GetSet("DoubleFace", false)
|
||||
BUILDER:GetSet("Skin", 0, {editor_onchange = function(self, num) return math.Round(math.max(tonumber(num), 0)) end})
|
||||
BUILDER:GetSet("DrawShadow", true)
|
||||
BUILDER:GetSet("LodOverride", -1)
|
||||
BUILDER:SetPropertyGroup("movement")
|
||||
BUILDER:GetSet("SprintSpeed", 0)
|
||||
BUILDER:GetSet("RunSpeed", 0)
|
||||
BUILDER:GetSet("WalkSpeed", 0)
|
||||
BUILDER:GetSet("CrouchSpeed", 0)
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:PropertyOrder("AimPartName")
|
||||
BUILDER:PropertyOrder("Bone")
|
||||
BUILDER:PropertyOrder("Position")
|
||||
BUILDER:PropertyOrder("Angles")
|
||||
BUILDER:PropertyOrder("EyeAngles")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Scale", Vector(1,1,1))
|
||||
BUILDER:SetPropertyGroup("behavior")
|
||||
BUILDER:GetSet("RelativeBones", true)
|
||||
BUILDER:GetSet("Weapon", false)
|
||||
BUILDER:GetSet("InverseKinematics", false)
|
||||
BUILDER:GetSet("MuteFootsteps", false)
|
||||
BUILDER:GetSet("SuppressFrames", false)
|
||||
BUILDER:GetSet("AnimationRate", 1)
|
||||
BUILDER:GetSet("FallApartOnDeath", false)
|
||||
BUILDER:GetSet("DeathRagdollizeParent", false)
|
||||
BUILDER:GetSet("HideRagdollOnDeath", false)
|
||||
BUILDER:GetSetPart("EyeTarget")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local ent_fields = {}
|
||||
|
||||
function BUILDER:EntityField(name, field)
|
||||
|
||||
field = "pac_" .. field
|
||||
|
||||
ent_fields[field] = name
|
||||
|
||||
self.PART["Set" .. name] = function(self, val)
|
||||
self[name] = val
|
||||
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
owner[field] = val
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
BUILDER:EntityField("InverseKinematics", "enable_ik")
|
||||
BUILDER:EntityField("MuteFootsteps", "mute_footsteps")
|
||||
BUILDER:EntityField("AnimationRate", "global_animation_rate")
|
||||
|
||||
BUILDER:EntityField("RunSpeed", "run_speed")
|
||||
BUILDER:EntityField("WalkSpeed", "walk_speed")
|
||||
BUILDER:EntityField("CrouchSpeed", "crouch_speed")
|
||||
BUILDER:EntityField("SprintSpeed", "sprint_speed")
|
||||
|
||||
BUILDER:EntityField("FallApartOnDeath", "death_physics_parts")
|
||||
BUILDER:EntityField("DeathRagdollizeParent", "death_ragdollize")
|
||||
BUILDER:EntityField("HideRagdollOnDeath", "death_hide_ragdoll")
|
||||
BUILDER:EntityField("DrawPlayerOnDeath", "draw_player_on_death")
|
||||
BUILDER:EntityField("HidePhysgunBeam", "hide_physgun_beam")
|
||||
|
||||
BUILDER:EntityField("MuteSounds", "mute_sounds")
|
||||
BUILDER:EntityField("AllowOggWhenMuted", "allow_ogg_sounds")
|
||||
BUILDER:EntityField("HideBullets", "hide_bullets")
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetColor(self:GetColor())
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if ent:IsPlayer() then
|
||||
return ent:Nick()
|
||||
else
|
||||
return language.GetPhrase(ent:GetClass())
|
||||
end
|
||||
end
|
||||
|
||||
if self.Weapon then
|
||||
return "Weapon"
|
||||
end
|
||||
|
||||
return self.ClassName
|
||||
end
|
||||
|
||||
|
||||
function PART:SetUseLegacyScale(b)
|
||||
self.UseLegacyScale = b
|
||||
self:UpdateScale()
|
||||
end
|
||||
|
||||
function PART:SetWeapon(b)
|
||||
self.Weapon = b
|
||||
if b then
|
||||
self:OnShow()
|
||||
else
|
||||
self:OnHide()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetDrawShadow(b)
|
||||
self.DrawShadow = b
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
ent:DrawShadow(b)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateScale(ent)
|
||||
ent = ent or self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if not self.UseLegacyScale then
|
||||
ent.pac3_Scale = self.Size
|
||||
end
|
||||
|
||||
if ent:IsPlayer() or ent:IsNPC() then
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "size", ent, self.Size)
|
||||
end
|
||||
pac.SetModelScale(ent, self.Scale)
|
||||
else
|
||||
pac.SetModelScale(ent, self.Scale * self.Size)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSize(val)
|
||||
self.Size = val
|
||||
self:UpdateScale()
|
||||
end
|
||||
|
||||
function PART:SetScale(val)
|
||||
self.Scale = val
|
||||
self:UpdateScale()
|
||||
end
|
||||
|
||||
function PART:SetColor(var)
|
||||
var = var or Vector(255, 255, 255)
|
||||
|
||||
self.Color = var
|
||||
self.Colorf = Vector(var.r, var.g, var.b) / 255
|
||||
|
||||
self.Colorc = self.Colorc or Color(var.r, var.g, var.b, self.Alpha)
|
||||
self.Colorc.r = var.r
|
||||
self.Colorc.g = var.g
|
||||
self.Colorc.b = var.b
|
||||
end
|
||||
|
||||
function PART:SetAlpha(var)
|
||||
self.Alpha = var
|
||||
|
||||
self.Colorc = self.Colorc or Color(self.Color.r, self.Color.g, self.Color.b, self.Alpha)
|
||||
self.Colorc.a = var
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var) then
|
||||
if var == "" then
|
||||
self.Materialm = nil
|
||||
else
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
self.Material = var
|
||||
end
|
||||
|
||||
function PART:SetRelativeBones(b)
|
||||
self.RelativeBones = b
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
self:UpdateScale(ent)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateWeaponDraw(ent)
|
||||
local wep = ent and ent:IsValid() and ent.GetActiveWeapon and ent:GetActiveWeapon() or NULL
|
||||
|
||||
if wep:IsWeapon() then
|
||||
if not wep.pac_weapon_class then
|
||||
wep:SetNoDraw(not self.DrawWeapon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateColor()
|
||||
render_SetColorModulation(self.Colorf.r * self.Brightness, self.Colorf.g * self.Brightness, self.Colorf.b * self.Brightness)
|
||||
if pac.drawing_motionblur_alpha then return end
|
||||
render_SetBlend(self.Alpha)
|
||||
end
|
||||
|
||||
function PART:UpdateMaterial()
|
||||
local mat = self.MaterialOverride or self.Materialm
|
||||
if mat then
|
||||
render_MaterialOverride(mat)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:UpdateAll(ent)
|
||||
self:UpdateColor(ent)
|
||||
self:UpdateMaterial(ent)
|
||||
self:UpdateScale(ent)
|
||||
end
|
||||
|
||||
local angle_origin = Angle()
|
||||
|
||||
local function setup_suppress()
|
||||
local last_framenumber = 0
|
||||
local current_frame = 0
|
||||
local current_frame_count = 0
|
||||
|
||||
return function()
|
||||
local frame_number = FrameNumber()
|
||||
|
||||
if frame_number == last_framenumber then
|
||||
current_frame = current_frame + 1
|
||||
else
|
||||
last_framenumber = frame_number
|
||||
|
||||
if current_frame_count ~= current_frame then
|
||||
current_frame_count = current_frame
|
||||
end
|
||||
|
||||
current_frame = 1
|
||||
end
|
||||
|
||||
return current_frame < current_frame_count
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
self:SetModel(self:GetModel())
|
||||
|
||||
if self.Weapon and ent.GetActiveWeapon and ent:GetActiveWeapon():IsValid() then
|
||||
ent = ent:GetActiveWeapon()
|
||||
end
|
||||
|
||||
for _, field in pairs(ent_fields) do
|
||||
self["Set" .. field](self, self[field])
|
||||
end
|
||||
|
||||
self:SetColor(self:GetColor())
|
||||
ent:SetColor(self.Colorc)
|
||||
self:UpdateWeaponDraw(self:GetOwner())
|
||||
|
||||
function ent.RenderOverride()
|
||||
if self:IsValid() then
|
||||
if not self.HideEntity then
|
||||
if self.SuppressFrames then
|
||||
if not self.should_suppress then
|
||||
self.should_suppress = setup_suppress()
|
||||
end
|
||||
|
||||
if self.should_suppress() then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self:ModifiersPostEvent("PreDraw")
|
||||
self:PreEntityDraw(ent)
|
||||
|
||||
local modpos = not self.Position:IsZero() or not self.Angles:IsZero()
|
||||
local pos
|
||||
|
||||
self.BoneOverride = nil
|
||||
|
||||
if modpos then
|
||||
pos = ent:GetPos()
|
||||
|
||||
self.BoneOverride = "none"
|
||||
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
ent:SetPos(pos)
|
||||
ent:SetRenderAngles(ang)
|
||||
pac.SetupBones(ent)
|
||||
end
|
||||
|
||||
ent:SetSkin(self.Skin)
|
||||
|
||||
if ent.pac_bodygroups_torender then
|
||||
for bgID, bgVal in pairs(ent.pac_bodygroups_torender) do
|
||||
ent:SetBodygroup(bgID, bgVal)
|
||||
end
|
||||
end
|
||||
|
||||
ent.pac_bodygroups_torender = nil
|
||||
|
||||
if self.EyeTarget.GetWorldPosition then
|
||||
ent:SetEyeTarget(self.EyeTarget:GetWorldPosition())
|
||||
end
|
||||
|
||||
ent:DrawModel()
|
||||
|
||||
if modpos then
|
||||
ent:SetPos(pos)
|
||||
ent:SetRenderAngles(angle_origin)
|
||||
end
|
||||
|
||||
self:PostEntityDraw(ent)
|
||||
self:ModifiersPostEvent("OnDraw")
|
||||
end
|
||||
else
|
||||
ent.RenderOverride = nil
|
||||
end
|
||||
end
|
||||
|
||||
self.current_ro = ent.RenderOverride
|
||||
|
||||
self:UpdateScale()
|
||||
|
||||
if self.LodOverride ~= -1 then self:SetLodOverride(self.LodOverride) end
|
||||
end
|
||||
|
||||
local ALLOW_TO_MDL = CreateConVar('pac_allow_mdl', '1', CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Allow to use custom MDLs')
|
||||
local ALLOW_TO_USE_MDL = CreateConVar('pac_allow_mdl_entity', '1', CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Allow to use custom MDLs as Entity')
|
||||
|
||||
function PART:SetModel(path)
|
||||
self.Model = path
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
pac.ResetBoneCache(ent)
|
||||
|
||||
if path:find("^http") then
|
||||
local status, reason = hook.Run('PAC3AllowMDLDownload', self:GetPlayerOwner(), self, path)
|
||||
local status2, reason2 = hook.Run('PAC3AllowEntityMDLDownload', self:GetPlayerOwner(), self, path)
|
||||
|
||||
if ALLOW_TO_USE_MDL:GetBool() and ALLOW_TO_MDL:GetBool() and status ~= false and status2 ~= false then
|
||||
if ent == pac.LocalPlayer then
|
||||
pac.Message("downloading ", path, " to use as player model")
|
||||
end
|
||||
|
||||
pac.DownloadMDL(path, function(real_path)
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
pac.Message("finished downloading ", path)
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "model", ent, self.Model)
|
||||
end
|
||||
|
||||
ent:SetModel(real_path)
|
||||
|
||||
ent:SetSubMaterial()
|
||||
|
||||
for i = 0, #ent:GetBodyGroups() - 1 do
|
||||
ent:SetBodygroup(i, 0)
|
||||
end
|
||||
|
||||
pac.ResetBoneCache(ent)
|
||||
|
||||
for _, child in ipairs(self:GetChildrenList()) do
|
||||
child:OnShow(true)
|
||||
end
|
||||
end, function(err)
|
||||
pac.Message(err)
|
||||
self:SetError(err)
|
||||
end, self:GetPlayerOwner())
|
||||
|
||||
self.mdl_zip = true
|
||||
else
|
||||
local msg = reason2 or reason or "mdl is not allowed"
|
||||
self.loading = msg
|
||||
self:SetError(msg)
|
||||
pac.Message(self:GetPlayerOwner(), ' - mdl files are not allowed')
|
||||
end
|
||||
elseif self.Model ~= "" then
|
||||
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "model", ent, self.Model)
|
||||
end
|
||||
|
||||
ent:SetModel(self.Model)
|
||||
|
||||
|
||||
pac.RunNextFrame('entity updatemat ' .. tostring(ent), function()
|
||||
if not ent:IsValid() or not self:IsValid() then return end
|
||||
pac.ResetBoneCache(ent)
|
||||
ent:SetSubMaterial()
|
||||
|
||||
for i = 0, #ent:GetBodyGroups() - 1 do
|
||||
ent:SetBodygroup(i, 0)
|
||||
end
|
||||
|
||||
self:CallRecursive("CalcShowHide", true)
|
||||
end)
|
||||
|
||||
self.mdl_zip = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetLodOverride(num)
|
||||
self.LodOverride = num
|
||||
local owner = self:GetOwner()
|
||||
if owner:IsValid() then
|
||||
owner:SetLOD(num)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.pac_mute_footsteps = self.MuteFootsteps
|
||||
|
||||
if self.Weapon and ent.GetActiveWeapon and ent:GetActiveWeapon():IsValid() then
|
||||
ent = ent:GetActiveWeapon()
|
||||
end
|
||||
|
||||
-- holy shit why does shooting reset the scale in singleplayer
|
||||
-- dumb workaround
|
||||
if game_SinglePlayer() and ent:IsPlayer() and ent:GetModelScale() ~= self.Size then
|
||||
self:UpdateScale(ent)
|
||||
end
|
||||
|
||||
if (self.HideEntity or self.Weapon) and self.current_ro ~= ent.RenderOverride then
|
||||
self:OnShow()
|
||||
end
|
||||
|
||||
ent.pac_material = self.Material
|
||||
ent.pac_materialm = self.Materialm
|
||||
ent.pac_color = self.Colorf
|
||||
ent.pac_alpha = self.Alpha
|
||||
ent.pac_brightness = self.Brightness
|
||||
|
||||
ent.pac_hide_entity = self.HideEntity
|
||||
ent.pac_fullbright = self.Fullbright
|
||||
ent.pac_invert = self.Invert
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
pac.emut.RestoreMutations(self:GetPlayerOwner(), "model", ent)
|
||||
pac.emut.RestoreMutations(self:GetPlayerOwner(), "size", ent)
|
||||
end
|
||||
|
||||
pac.SetModelScale(ent)
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if self.Weapon and ent.GetActiveWeapon and ent:GetActiveWeapon():IsValid() then
|
||||
ent = ent:GetActiveWeapon()
|
||||
end
|
||||
|
||||
ent.RenderOverride = nil
|
||||
ent:SetColor(Color(255, 255, 255, 255))
|
||||
|
||||
ent.pac_material = nil
|
||||
ent.pac_materialm = nil
|
||||
ent.pac_color = nil
|
||||
ent.pac_alpha = nil
|
||||
ent.pac_brightness = nil
|
||||
|
||||
ent.pac_hide_entity = nil
|
||||
ent.pac_fullbright = nil
|
||||
ent.pac_invert = nil
|
||||
|
||||
for key in pairs(ent_fields) do
|
||||
ent[key] = nil
|
||||
end
|
||||
|
||||
if ent:IsPlayer() or ent:IsNPC() then
|
||||
-- do nothing, we want the player to feel small even on hide
|
||||
else
|
||||
pac.SetModelScale(ent, Vector(1,1,1))
|
||||
end
|
||||
|
||||
local weps = ent.GetWeapons and ent:GetWeapons()
|
||||
|
||||
if weps then
|
||||
for _, wep in pairs(weps) do
|
||||
if not wep.pac_weapon_class then
|
||||
wep:SetNoDraw(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.LodOverride ~= -1 then
|
||||
ent:SetLOD(-1)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetHideEntity(b)
|
||||
self.HideEntity = b
|
||||
if b then
|
||||
self:OnHide()
|
||||
else
|
||||
self:OnShow()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PreEntityDraw(ent)
|
||||
self:UpdateWeaponDraw(ent)
|
||||
|
||||
self:UpdateColor(ent)
|
||||
self:UpdateMaterial(ent)
|
||||
|
||||
if self.Invert then
|
||||
render_CullMode(1) -- MATERIAL_CULLMODE_CW
|
||||
end
|
||||
|
||||
if self.Fullbright then
|
||||
render_SuppressEngineLighting(true)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PostEntityDraw()
|
||||
if self.Invert then
|
||||
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
|
||||
end
|
||||
|
||||
if self.Fullbright then
|
||||
render_SuppressEngineLighting(false)
|
||||
end
|
||||
|
||||
render_SetBlend(1)
|
||||
render_SetColorModulation(1,1,1)
|
||||
|
||||
render_MaterialOverride()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
|
||||
do
|
||||
local IN_SPEED = IN_SPEED
|
||||
local IN_WALK = IN_WALK
|
||||
local IN_DUCK = IN_DUCK
|
||||
|
||||
local function mod_speed(cmd, speed)
|
||||
if speed and speed ~= 0 then
|
||||
local forward = cmd:GetForwardMove()
|
||||
forward = forward > 0 and speed or forward < 0 and -speed or 0
|
||||
|
||||
local side = cmd:GetSideMove()
|
||||
side = side > 0 and speed or side < 0 and -speed or 0
|
||||
|
||||
|
||||
cmd:SetForwardMove(forward)
|
||||
cmd:SetSideMove(side)
|
||||
end
|
||||
end
|
||||
|
||||
pac.AddHook("CreateMove", "legacy_entity_part_speed_modifier", function(cmd)
|
||||
if cmd:KeyDown(IN_SPEED) then
|
||||
mod_speed(cmd, pac.LocalPlayer.pac_sprint_speed)
|
||||
elseif cmd:KeyDown(IN_WALK) then
|
||||
mod_speed(cmd, pac.LocalPlayer.pac_walk_speed)
|
||||
elseif cmd:KeyDown(IN_DUCK) then
|
||||
mod_speed(cmd, pac.LocalPlayer.pac_crouch_speed)
|
||||
else
|
||||
mod_speed(cmd, pac.LocalPlayer.pac_run_speed)
|
||||
end
|
||||
end)
|
||||
end
|
||||
63
lua/pac3/core/client/parts/legacy/light.lua
Normal file
63
lua/pac3/core/client/parts/legacy/light.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "legacy light"
|
||||
PART.ClassName = "light"
|
||||
PART.Group = "legacy"
|
||||
|
||||
PART.Icon = 'icon16/lightbulb.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Brightness", 1)
|
||||
BUILDER:GetSet("Size", 5, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Style", 0, {editor_clamp = {0, 16}})
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local hue = pac.ColorToNames(self:GetColor())
|
||||
return hue .. " light"
|
||||
end
|
||||
|
||||
local DynamicLight = DynamicLight
|
||||
|
||||
function PART:OnDraw()
|
||||
local pos = self:GetDrawPosition()
|
||||
local light = self.light or DynamicLight(tonumber(self.UniqueID))
|
||||
|
||||
light.Pos = pos
|
||||
|
||||
light.MinLight = self.Brightness
|
||||
light.Size = self.Size
|
||||
light.Style = self.Style
|
||||
|
||||
light.r = self.Color.r
|
||||
light.g = self.Color.g
|
||||
light.b = self.Color.b
|
||||
|
||||
-- 100000000 constant is better than calling pac.RealTime
|
||||
light.DieTime = 1000000000000 -- pac.RealTime
|
||||
|
||||
self.light = light
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local light = self.light
|
||||
if light then
|
||||
light.DieTime = 0
|
||||
light.Size = 0
|
||||
light.MinLight = 0
|
||||
light.Pos = Vector()
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
484
lua/pac3/core/client/parts/legacy/material.lua
Normal file
484
lua/pac3/core/client/parts/legacy/material.lua
Normal file
@@ -0,0 +1,484 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.FriendlyName = "legacy material"
|
||||
PART.ClassName = "material"
|
||||
|
||||
PART.Icon = 'icon16/paintcan.png'
|
||||
PART.Group = "legacy"
|
||||
|
||||
local group_ordering = {
|
||||
{pattern = "phong", group = "phong"},
|
||||
{pattern = "envmap", group = "env map"},
|
||||
{pattern = {"ambientocclusion", "halflambert"}, group = "ambient occlusion"},
|
||||
{pattern = "detail", group = "detail"},
|
||||
{pattern = "rimlight", group = "rimlight"},
|
||||
{pattern = {"cloak", "refract"}, group = "cloak"},
|
||||
{pattern = "color", group = "colors"},
|
||||
{pattern = {"bumpmap", "basetexture", "^envmapmask$", "lightwarptexture"}, group = "textures"},
|
||||
{pattern = "flesh", group = "flesh"},
|
||||
{pattern = "selfillum", group = "selfillum"},
|
||||
{pattern = "emissive", group ="emissive"},
|
||||
}
|
||||
|
||||
PART.ShaderParams =
|
||||
{
|
||||
BaseTexture = "ITexture",
|
||||
|
||||
CloakPassEnabled = "boolean",
|
||||
CloakFactor = {type = "number", extra = {editor_sensitivity = 0.25, editor_clamp = {0, 1}}},
|
||||
CloakColorTint = "Vector",
|
||||
RefractAmount = "number",
|
||||
|
||||
BumpMap = "ITexture",
|
||||
LightWarpTexture = "ITexture",
|
||||
|
||||
Detail = "ITexture",
|
||||
DetailTint = "Vector",
|
||||
DetailScale = "number",
|
||||
DetailBlendMode = {type = "number", extra = {editor_onchange = function(pnl, num) return math.Round(math.max(num, 0)) end}},
|
||||
DetailBlendFactor = "number",
|
||||
|
||||
Phong = "boolean",
|
||||
PhongBoost = "number",
|
||||
PhongExponent = "number",
|
||||
PhongTint = "Vector",
|
||||
PhongFresnelRanges = {type = "Vector", extra = {editor_panel = "color"}},
|
||||
PhongWarpTexture = "ITexture",
|
||||
PhongAlbedoTint = "boolean",
|
||||
PhongExponentTexture = "ITexture",
|
||||
|
||||
Rimlight = "boolean",
|
||||
RimlightBoost = "number",
|
||||
RimlightExponent = "number",
|
||||
|
||||
-- doesn't do anything i think
|
||||
EnvMap = "ITexture",
|
||||
EnvMapMask = "ITexture",
|
||||
EnvMapTint = "Vector",
|
||||
EnvMapMode = "number",
|
||||
EnvMapContrast = "number",
|
||||
EnvMapMaskScale = "number",
|
||||
EnvMapSaturation = "Vector",
|
||||
NormalMapAlphaEnvMapMask = "boolean",
|
||||
BaseAlphaEnvMapMask = "boolean",
|
||||
Selfillum_EnvMapMask_Alpha = "number",
|
||||
|
||||
AmbientOcclusion = "boolean",
|
||||
AmbientOcclusionColor = "Vector",
|
||||
AmbientOcclusionTexture = "ITexture",
|
||||
|
||||
BlendTintByBaseAlpha = "boolean",
|
||||
BlendTintColorOverBase = "Vector",
|
||||
ColorTint_Base = "Vector",
|
||||
ColorTint_Tmp = "Vector",
|
||||
Color = "Vector",
|
||||
Color2 = "Vector",
|
||||
Additive = "boolean",
|
||||
AlphaTest = "boolean",
|
||||
TranslucentX = "boolean",
|
||||
|
||||
|
||||
HalfLambert = "boolean",
|
||||
|
||||
Selfillum = "boolean",
|
||||
SelfillumTint = "Vector",
|
||||
SelfillumMask = "ITexture",
|
||||
Selfillum_Envmapmask_Alpha = "ITexture",
|
||||
SelfillumFresnel = "boolean",
|
||||
SelfillumFresnlenMinMaxExp = "Vector",
|
||||
|
||||
FleshInteriorEnabled = "boolean", --"0", "Enable Flesh interior blend pass" )
|
||||
FleshInteriorTexture = "ITexture", --"", "Flesh color texture" )
|
||||
FleshInteriorNoiseTexture = "ITexture", --"", "Flesh noise texture" )
|
||||
FleshBorderTexture1D = "ITexture", --"", "Flesh border 1D texture" )
|
||||
FleshNormalTexture = "ITexture", --"", "Flesh normal texture" )
|
||||
FleshSubsurfaceTexture = "ITexture", --"", "Flesh subsurface texture" )
|
||||
FleshCubeTexture = "ITexture", --"", "Flesh cubemap texture" )
|
||||
FleshBorderNoiseScale = "number", --"1.5", "Flesh Noise UV scalar for border" )
|
||||
FleshDebugForceFleshOn = "boolean", --"0", "Flesh Debug full flesh" )
|
||||
|
||||
--FleshEFFECTCENTERRADIUS1, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0.001]", "Flesh effect center and radius" )
|
||||
--FleshEFFECTCENTERRADIUS2, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0.001]", "Flesh effect center and radius" )
|
||||
--FleshEFFECTCENTERRADIUS3, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0.001]", "Flesh effect center and radius" )
|
||||
--FleshEFFECTCENTERRADIUS4, SHADER_PARAM_TYPE_VEC4, "[0 0 0 0.001]", "Flesh effect center and radius" )
|
||||
|
||||
FleshSubsurfaceTint = "Vector", --"[1 1 1]", "Subsurface Color" )
|
||||
FleshBorderWidth = "number", --"0.3", "Flesh border" )
|
||||
FleshBorderSoftness = "number", --"0.42", "Flesh border softness (> 0.0 && <= 0.5)" )
|
||||
FleshBorderTint = "Vector", --"[1 1 1]", "Flesh border Color" )
|
||||
FleshGlobalOpacity = "number", --"1.0", "Flesh global opacity" )
|
||||
FleshGlossBrightness = "number", --"0.66", "Flesh gloss brightness" )
|
||||
FleshScrollSpeed = "number", --"1.0", "Flesh scroll speed" )
|
||||
|
||||
EmissiveBlendEnabled = "boolean",
|
||||
EmissiveBlendTexture = "ITexture",
|
||||
EmissiveBlendBaseTexture = "ITexture",
|
||||
EmissiveBlendFlowTexture = "ITexture",
|
||||
EmissiveBlendTint = "Vector",
|
||||
EmissiveBlendScrollVector = "Vector",
|
||||
|
||||
DistanceAlpha = "number",
|
||||
VertexAlpha = "boolean",
|
||||
Alpha = "number",
|
||||
}
|
||||
|
||||
function PART:OnThink()
|
||||
if self.delay_set and self.Parent then
|
||||
self.delay_set()
|
||||
self.delay_set = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function setup(PART)
|
||||
local sorted = {}
|
||||
for k,v in pairs(PART.ShaderParams) do
|
||||
table.insert(sorted, {k = k, v = v})
|
||||
end
|
||||
table.sort(sorted, function(a, b) return a.k > b.k end)
|
||||
|
||||
for pass = 1, 2 do
|
||||
for _, info in ipairs(group_ordering) do
|
||||
for _, v in ipairs(sorted) do
|
||||
local name, T = v.k, v.v
|
||||
|
||||
local found
|
||||
|
||||
if istable(info.pattern) then
|
||||
for k,v in pairs(info.pattern) do
|
||||
if name:lower():find(v) then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
found = name:lower():find(info.pattern)
|
||||
end
|
||||
|
||||
if pass == 1 then
|
||||
if found then
|
||||
BUILDER:SetPropertyGroup(info.group)
|
||||
else
|
||||
goto CONTINUE
|
||||
end
|
||||
elseif pass == 2 then
|
||||
if not found then
|
||||
BUILDER:SetPropertyGroup()
|
||||
else
|
||||
goto CONTINUE
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local extra
|
||||
if istable(T) then
|
||||
extra = T.extra
|
||||
T = T.type
|
||||
end
|
||||
if T == "ITexture" then
|
||||
BUILDER:GetSet(name, "", {editor_panel = "textures"})
|
||||
|
||||
PART["Set" .. name] = function(self, var)
|
||||
self[name] = var
|
||||
|
||||
if
|
||||
self.SKIP or
|
||||
pac.Handleurltex(
|
||||
self,
|
||||
var,
|
||||
function(_, tex)
|
||||
local mat = self:GetMaterialFromParent()
|
||||
if mat then
|
||||
mat:SetTexture("$" .. name, tex)
|
||||
|
||||
self.SKIP = true
|
||||
self:UpdateMaterial()
|
||||
self.SKIP = false
|
||||
else
|
||||
self.delay_set = function()
|
||||
local mat = self:GetMaterialFromParent()
|
||||
if mat then
|
||||
mat:SetTexture("$" .. name, tex)
|
||||
self.SKIP = true
|
||||
self:UpdateMaterial()
|
||||
self.SKIP = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
local mat = self:GetMaterialFromParent()
|
||||
|
||||
if mat then
|
||||
if var ~= "" then
|
||||
local _mat = Material(var)
|
||||
local tex = _mat:GetTexture("$" .. name)
|
||||
|
||||
if not tex or tex:GetName() == "error" then
|
||||
tex = pac.CreateMaterial("pac3_tex_" .. var .. "_" .. self.Id, "VertexLitGeneric", {["$basetexture"] = var}):GetTexture("$basetexture")
|
||||
|
||||
if not tex or tex:GetName() == "error" then
|
||||
tex = _mat:GetTexture("$basetexture")
|
||||
end
|
||||
end
|
||||
|
||||
if tex then
|
||||
mat:SetTexture("$" .. name, tex)
|
||||
end
|
||||
else
|
||||
mat:SetUndefined("$" .. name)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif T == "boolean" then
|
||||
BUILDER:GetSet(name, false)
|
||||
|
||||
PART["Set" .. name] = function(self, var)
|
||||
self[name] = var
|
||||
|
||||
local mat = self:GetMaterialFromParent()
|
||||
|
||||
if mat then
|
||||
if name == "TranslucentX" then
|
||||
name = "Translucent"
|
||||
end
|
||||
|
||||
mat:SetInt("$" .. name, var and 1 or 0) -- setint crashes?
|
||||
end
|
||||
end
|
||||
elseif T == "number" then
|
||||
BUILDER:GetSet(name, 0)
|
||||
|
||||
PART["Set" .. name] = function(self, var)
|
||||
self[name] = var
|
||||
|
||||
local mat = self:GetMaterialFromParent()
|
||||
|
||||
if mat then
|
||||
mat:SetFloat("$" .. name, var)
|
||||
end
|
||||
end
|
||||
elseif T == "Vector" then
|
||||
local def = Vector(0,0,0)
|
||||
|
||||
-- hack
|
||||
local key = name:lower()
|
||||
if key == "color" or key == "color2" then
|
||||
def = Vector(1,1,1)
|
||||
end
|
||||
|
||||
BUILDER:GetSet(name, def)
|
||||
|
||||
PART["Set" .. name] = function(self, var)
|
||||
self[name] = var
|
||||
|
||||
local mat = self:GetMaterialFromParent()
|
||||
|
||||
if mat then
|
||||
if key == "color" or key == "color2" then
|
||||
timer.Simple(0.1, function() mat:SetVector("$" .. name, var) end)
|
||||
end
|
||||
|
||||
mat:SetVector("$" .. name, var)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_transform(texture_name)
|
||||
local position_key = texture_name.."Position"
|
||||
local scale_key = texture_name.."Scale"
|
||||
local angle_key = texture_name.."Angle"
|
||||
local angle_center_key = texture_name.."AngleCenter"
|
||||
|
||||
BUILDER:GetSet(position_key, Vector(0, 0, 0))
|
||||
BUILDER:GetSet(scale_key, Vector(1, 1, 1))
|
||||
BUILDER:GetSet(angle_key, 0, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet(angle_center_key, Vector(0.5, 0.5, 0))
|
||||
|
||||
PART.TransformVars = PART.TransformVars or {}
|
||||
PART.TransformVars[position_key] = true
|
||||
PART.TransformVars[scale_key] = true
|
||||
PART.TransformVars[angle_key] = true
|
||||
PART.TransformVars[angle_center_key] = true
|
||||
|
||||
local shader_key = "$"..texture_name.."transform"
|
||||
|
||||
local function setup_matrix(self)
|
||||
self.matrix = self.matrix or Matrix()
|
||||
self.translation_vector = self.translation_vector or Vector(0, 0, 0)
|
||||
self.rotation_angle = self.rotation_angle or Angle(0, 0, 0)
|
||||
|
||||
self.matrix:Identity()
|
||||
self.matrix:Translate(self.translation_vector)
|
||||
|
||||
self.matrix:Translate(self[angle_center_key])
|
||||
self.matrix:Rotate(self.rotation_angle)
|
||||
self.matrix:Translate(-self[angle_center_key])
|
||||
|
||||
self.matrix:SetScale(self[scale_key])
|
||||
end
|
||||
|
||||
PART["Set" .. position_key] = function(self, vec)
|
||||
self[position_key] = vec
|
||||
setup_matrix(self)
|
||||
|
||||
self.translation_vector.x = self[position_key].x%1
|
||||
self.translation_vector.y = self[position_key].y%1
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. scale_key] = function(self, vec)
|
||||
self[scale_key] = vec
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. angle_key] = function(self, num)
|
||||
self[angle_key] = num
|
||||
setup_matrix(self)
|
||||
|
||||
self.rotation_angle.y = self[angle_key]*360
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. angle_center_key] = function(self, vec)
|
||||
self[angle_center_key] = vec
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
|
||||
setup(PART)
|
||||
add_transform("BaseTexture")
|
||||
--add_transform("Bump") -- doesn't work
|
||||
--add_transform("EnvMapMask")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetMaterialFromParent()
|
||||
if self:GetParent():IsValid() then
|
||||
if not self.Materialm then
|
||||
local mat = pac.CreateMaterial(pac.uid"pac_material_", "VertexLitGeneric", {})
|
||||
|
||||
if self.Parent.Materialm then
|
||||
local tex
|
||||
tex = self.Parent.Materialm:GetTexture("$bumpmap")
|
||||
if tex and not tex:IsError() then
|
||||
mat:SetTexture("$bumpmap", tex)
|
||||
end
|
||||
|
||||
local tex = self.Parent.Materialm:GetTexture("$basetexture")
|
||||
if tex and not tex:IsError() then
|
||||
mat:SetTexture("$basetexture", tex)
|
||||
end
|
||||
end
|
||||
|
||||
self.Materialm = mat
|
||||
end
|
||||
|
||||
self.Parent.Materialm = self.Materialm
|
||||
|
||||
if self.Parent.UpdateSubMaterialId then
|
||||
self.Parent:UpdateSubMaterialId()
|
||||
end
|
||||
|
||||
return self.Materialm
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetTranslucent(b)
|
||||
self.Translucent = b
|
||||
self:UpdateMaterial()
|
||||
end
|
||||
|
||||
function PART:GetRawMaterial()
|
||||
if not self.Materialm then
|
||||
local mat = pac.CreateMaterial(pac.uid"pac_material_", "VertexLitGeneric", {})
|
||||
self.Materialm = mat
|
||||
end
|
||||
|
||||
return self.Materialm
|
||||
end
|
||||
|
||||
function PART:OnParent(parent)
|
||||
self:GetMaterialFromParent()
|
||||
end
|
||||
|
||||
function PART:UpdateMaterial(now)
|
||||
if not self:GetPlayerOwner():IsValid() then return end
|
||||
self:GetMaterialFromParent()
|
||||
|
||||
for key, val in pairs(self.StorableVars) do
|
||||
if self.ShaderParams[key] or self.TransformVars[key] then
|
||||
self["Set" .. key](self, self["Get"..key](self))
|
||||
end
|
||||
end
|
||||
|
||||
pac.UpdateMaterialParts("update", self:GetPlayerOwnerId(), self, self.Materialm)
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
if self:GetPlayerOwner():IsValid() then
|
||||
pac.UpdateMaterialParts("remove", self:GetPlayerOwnerId(), self, self.Materialm)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnMaterialChanged()
|
||||
if self.suppress_event then return end
|
||||
self:UpdateMaterial()
|
||||
end
|
||||
|
||||
function PART:OnParent(parent)
|
||||
self:UpdateMaterial()
|
||||
end
|
||||
|
||||
function PART:OnUnParent(parent)
|
||||
self.Materialm = nil
|
||||
self.updated = false
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local parent = self:GetParent()
|
||||
|
||||
if parent:IsValid() and parent.SetMaterial then
|
||||
self.suppress_event = true
|
||||
parent:SetMaterial(parent.Material)
|
||||
self.suppress_event = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self:UpdateMaterial()
|
||||
|
||||
local name = self.Name
|
||||
|
||||
pac.UpdateMaterialParts("show", self:GetPlayerOwnerId(), self, self.Name)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
900
lua/pac3/core/client/parts/legacy/model.lua
Normal file
900
lua/pac3/core/client/parts/legacy/model.lua
Normal file
@@ -0,0 +1,900 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
|
||||
local math_max = math.max
|
||||
local math_min = math.min
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
|
||||
local Matrix = Matrix
|
||||
local Vector = Vector
|
||||
|
||||
local cam_PushModelMatrix = cam.PushModelMatrix
|
||||
local cam_PopModelMatrix = cam.PopModelMatrix
|
||||
|
||||
local render = render
|
||||
local render_CullMode = render.CullMode
|
||||
local render_SetColorModulation = render.SetColorModulation
|
||||
local render_SetBlend = render.SetBlend
|
||||
local render_SetMaterial = render.SetMaterial
|
||||
local render_ModelMaterialOverride = render.MaterialOverride
|
||||
local render_MaterialOverride = render.ModelMaterialOverride
|
||||
local render_PopFilterMag = render.PopFilterMag
|
||||
local render_PopFilterMin = render.PopFilterMin
|
||||
local render_PopFlashlightMode = render.PopFlashlightMode
|
||||
local render_PushFilterMag = render.PushFilterMag
|
||||
local render_PushFilterMin = render.PushFilterMin
|
||||
local render_PushFlashlightMode = render.PushFlashlightMode
|
||||
local render_SuppressEngineLighting = render.SuppressEngineLighting
|
||||
|
||||
local IMaterial_GetFloat = FindMetaTable("IMaterial").GetFloat
|
||||
local IMaterial_GetVector = FindMetaTable("IMaterial").GetVector
|
||||
local IMaterial_SetFloat = FindMetaTable("IMaterial").SetFloat
|
||||
local IMaterial_SetVector = FindMetaTable("IMaterial").SetVector
|
||||
|
||||
local EF_BONEMERGE = EF_BONEMERGE
|
||||
|
||||
local MATERIAL_CULLMODE_CW = MATERIAL_CULLMODE_CW
|
||||
local MATERIAL_CULLMODE_CCW = MATERIAL_CULLMODE_CCW
|
||||
|
||||
local TEXFILTER = TEXFILTER
|
||||
local NULL = NULL
|
||||
local Color = Color
|
||||
|
||||
pac.DisableColoring = false
|
||||
pac.DisableDoubleFace = false
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "legacy model"
|
||||
PART.ClassName = "model"
|
||||
PART.Category = "model"
|
||||
PART.ManualDraw = true
|
||||
PART.HandleModifiersManually = true
|
||||
PART.Icon = 'icon16/shape_square.png'
|
||||
PART.Group = "legacy"
|
||||
PART.ThinkTime = 0.5
|
||||
|
||||
PART.is_model_part = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("Model", "models/dav0r/hoverball.mdl", {editor_panel = "model"})
|
||||
BUILDER:GetSet("Material", "", {editor_panel = "material"})
|
||||
BUILDER:GetSet("UseLegacyScale", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:PropertyOrder("AimPartName")
|
||||
BUILDER:PropertyOrder("Bone")
|
||||
BUILDER:GetSet("BoneMerge", false)
|
||||
BUILDER:PropertyOrder("Position")
|
||||
BUILDER:PropertyOrder("Angles")
|
||||
BUILDER:PropertyOrder("EyeAngles")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Scale", Vector(1,1,1))
|
||||
BUILDER:PropertyOrder("PositionOffset")
|
||||
BUILDER:PropertyOrder("AngleOffset")
|
||||
BUILDER:GetSet("AlternativeScaling", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Brightness", 1)
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
BUILDER:GetSet("Fullbright", false)
|
||||
BUILDER:GetSet("CellShade", 0, {editor_sensitivity = 0.1})
|
||||
BUILDER:PropertyOrder("Translucent")
|
||||
BUILDER:GetSet("Invert", false)
|
||||
BUILDER:GetSet("DoubleFace", false)
|
||||
BUILDER:GetSet("Skin", 0, {editor_onchange = function(self, num) return math.Round(math.max(tonumber(num), 0)) end})
|
||||
BUILDER:GetSet("LodOverride", -1)
|
||||
BUILDER:GetSet("Passes", 1)
|
||||
BUILDER:GetSet("TintColor", Vector(0, 0, 0), {editor_panel = "color"})
|
||||
BUILDER:GetSet("LightBlend", 1)
|
||||
BUILDER:GetSet("ModelFallback", "", {editor_panel = "model"})
|
||||
BUILDER:GetSet("TextureFilter", 3)
|
||||
BUILDER:GetSet("BlurLength", 0)
|
||||
BUILDER:GetSet("BlurSpacing", 0)
|
||||
BUILDER:GetSet("UsePlayerColor", false)
|
||||
BUILDER:GetSet("UseWeaponColor", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
BUILDER:GetSet("OwnerEntity", false)
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName(("/" .. self:GetModel()):match(".+/(.-)%."))
|
||||
|
||||
return str and str:gsub("%d", "") or "error"
|
||||
end
|
||||
|
||||
function PART:Reset()
|
||||
self:Initialize(self.is_obj)
|
||||
for _, key in pairs(self:GetStorableVars()) do
|
||||
if PART[key] then
|
||||
self["Set" .. key](self, self["Get" .. key](self))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetUseLegacyScale(b)
|
||||
self.UseLegacyScale = b
|
||||
if not b then
|
||||
self.requires_bone_model_scale = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetTextureFilter(num)
|
||||
self.TextureFilter = num
|
||||
|
||||
self.texfilter_enum = math.Clamp(math.Round(num), 0, 3)
|
||||
end
|
||||
|
||||
function PART:Initialize(is_obj)
|
||||
self.Owner = pac.CreateEntity(self:GetModel(), is_obj)
|
||||
if not self.Owner:IsValid() then
|
||||
pac.Message("pac3 failed to create entity!")
|
||||
return
|
||||
end
|
||||
self.Owner:SetNoDraw(true)
|
||||
self.Owner.PACPart = self
|
||||
self.is_obj = is_obj
|
||||
end
|
||||
|
||||
|
||||
function PART:OnBecomePhysics()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
ent:PhysicsInit(SOLID_NONE)
|
||||
ent:SetMoveType(MOVETYPE_NONE)
|
||||
ent:SetNoDraw(true)
|
||||
ent.RenderOverride = nil
|
||||
|
||||
self.skip_orient = false
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local owner = self:GetParentOwner()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and owner:IsValid() and owner ~= ent then
|
||||
ent:SetPos(owner:EyePos())
|
||||
|
||||
if self.OwnerEntity then
|
||||
self:SetOwnerEntity(self.OwnerEntity)
|
||||
end
|
||||
end
|
||||
|
||||
if self.BlurLength > 0 then
|
||||
self.blur_history = {}
|
||||
self.blur_last_add = 0
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
function PART:OnThink()
|
||||
pac.SetModelScale(self:GetOwner(), self.Scale * self.Size, nil, self.UseLegacyScale)
|
||||
|
||||
self:CheckScale()
|
||||
self:CheckBoneMerge()
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
ent.pac_matproxies = ent.pac_matproxies or {}
|
||||
ent.pac_matproxies.ItemTintColor = self.TintColor / 255
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local NULL = NULL
|
||||
|
||||
local function BIND_MATPROXY(NAME, TYPE)
|
||||
|
||||
local set = "Set" .. TYPE
|
||||
|
||||
matproxy.Add(
|
||||
{
|
||||
name = NAME,
|
||||
|
||||
init = function(self, mat, values)
|
||||
self.result = values.resultvar
|
||||
end,
|
||||
|
||||
bind = function(self, mat, ent)
|
||||
ent = ent or NULL
|
||||
if ent:IsValid() then
|
||||
if ent.pac_matproxies and ent.pac_matproxies[NAME] then
|
||||
mat[set](mat, self.result, ent.pac_matproxies[NAME])
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
-- tf2
|
||||
BIND_MATPROXY("ItemTintColor", "Vector")
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetOwnerEntity(b)
|
||||
local ent = self:GetParentOwner()
|
||||
if ent:IsValid() then
|
||||
if b then
|
||||
self.Owner = ent
|
||||
|
||||
function ent.RenderOverride()
|
||||
if self:IsValid() then
|
||||
if not self.HideEntity then
|
||||
self:PreEntityDraw(ent, ent:GetPos(), ent:GetAngles())
|
||||
ent:DrawModel()
|
||||
self:PostEntityDraw(ent, ent:GetPos(), ent:GetAngles())
|
||||
end
|
||||
else
|
||||
ent.RenderOverride = nil
|
||||
end
|
||||
end
|
||||
elseif self.OwnerEntity then
|
||||
self.Owner = NULL
|
||||
|
||||
ent.RenderOverride = nil
|
||||
pac.SetModelScale(ent, Vector(1,1,1), nil, self.UseLegacyScale)
|
||||
|
||||
self:Initialize()
|
||||
end
|
||||
end
|
||||
|
||||
self.OwnerEntity = b
|
||||
end
|
||||
|
||||
function PART:PreEntityDraw(ent, pos, ang)
|
||||
if not ent:IsPlayer() and pos and ang then
|
||||
if not self.skip_orient then
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
end
|
||||
end
|
||||
|
||||
if self.Alpha ~= 0 and self.Size ~= 0 then
|
||||
self:ModifiersPreEvent("OnDraw")
|
||||
|
||||
if not pac.DisableDoubleFace and (self.DoubleFace or self.Invert) then
|
||||
render_CullMode(MATERIAL_CULLMODE_CW)
|
||||
end
|
||||
|
||||
if not pac.DisableColoring then
|
||||
if not self.Colorf then
|
||||
self:SetColor(self:GetColor())
|
||||
end
|
||||
|
||||
if self.UseWeaponColor or self.UsePlayerColor then
|
||||
local ply = self:GetPlayerOwner()
|
||||
if ply:IsValid() then
|
||||
local c
|
||||
|
||||
c = ply:GetPlayerColor()
|
||||
if c ~= self.last_playercolor then
|
||||
self:SetColor(self:GetColor())
|
||||
self.last_playercolor = c
|
||||
end
|
||||
|
||||
c = ply:GetWeaponColor()
|
||||
if c ~= self.last_weaponcolor then
|
||||
self:SetColor(self:GetColor())
|
||||
self.last_weaponcolor = c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Save existing material color and alpha
|
||||
if self.Materialm then
|
||||
-- this is so bad for GC performance
|
||||
self.OriginalMaterialColor = self.OriginalMaterialColor or IMaterial_GetVector (self.Materialm, "$color")
|
||||
self.OriginalMaterialAlpha = self.OriginalMaterialAlpha or IMaterial_GetFloat (self.Materialm, "$alpha")
|
||||
end
|
||||
|
||||
local r, g, b = self.Colorf[1], self.Colorf[2], self.Colorf[3]
|
||||
|
||||
if self.LightBlend ~= 1 then
|
||||
local v = render.GetLightColor(pos)
|
||||
r = r * v.r * self.LightBlend
|
||||
g = g * v.g * self.LightBlend
|
||||
b = b * v.b * self.LightBlend
|
||||
|
||||
v = render.GetAmbientLightColor(pos)
|
||||
r = r * v.r * self.LightBlend
|
||||
g = g * v.g * self.LightBlend
|
||||
b = b * v.b * self.LightBlend
|
||||
end
|
||||
|
||||
-- render.SetColorModulation and render.SetAlpha set the material $color and $alpha.
|
||||
render_SetColorModulation(r,g,b)
|
||||
render_SetBlend(self.Alpha)
|
||||
end
|
||||
|
||||
if self.Fullbright then
|
||||
render_SuppressEngineLighting(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local DEFAULT_COLOR = Vector(1, 1, 1)
|
||||
local WHITE = Material("models/debug/debugwhite")
|
||||
|
||||
function PART:PostEntityDraw(ent, pos, ang)
|
||||
if self.Alpha ~= 0 and self.Size ~= 0 then
|
||||
if not pac.DisableDoubleFace then
|
||||
if self.DoubleFace then
|
||||
render_CullMode(MATERIAL_CULLMODE_CCW)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
elseif self.Invert then
|
||||
render_CullMode(MATERIAL_CULLMODE_CCW)
|
||||
end
|
||||
end
|
||||
|
||||
if self.Fullbright then
|
||||
render_SuppressEngineLighting(false)
|
||||
end
|
||||
|
||||
if self.CellShade > 0 then
|
||||
self:CheckScale()
|
||||
self:CheckBoneMerge()
|
||||
|
||||
pac.SetModelScale(ent, self.Scale * self.Size * (1 + self.CellShade), nil, self.UseLegacyScale)
|
||||
render_CullMode(MATERIAL_CULLMODE_CW)
|
||||
render_SetColorModulation(0,0,0)
|
||||
render_SuppressEngineLighting(true)
|
||||
render_MaterialOverride(WHITE)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
render_MaterialOverride()
|
||||
render_SuppressEngineLighting(false)
|
||||
render_CullMode(MATERIAL_CULLMODE_CCW)
|
||||
pac.SetModelScale(ent, self.Scale * self.Size, nil, self.UseLegacyScale)
|
||||
end
|
||||
|
||||
-- Restore material color and alpha
|
||||
if self.Materialm then
|
||||
IMaterial_SetVector (self.Materialm, "$color", self.OriginalMaterialColor or DEFAULT_COLOR)
|
||||
IMaterial_SetFloat (self.Materialm, "$alpha", self.OriginalMaterialAlpha or 1)
|
||||
self.OriginalMaterialColor = nil
|
||||
self.OriginalMaterialAlpha = nil
|
||||
end
|
||||
|
||||
self:ModifiersPostEvent("OnDraw")
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then
|
||||
self:Reset()
|
||||
ent = self:GetOwner()
|
||||
if not ent:IsValid() then
|
||||
pac.Message("WTF", ent, self:GetOwner())
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
self:PreEntityDraw(ent, pos, ang)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
self:PostEntityDraw(ent, pos, ang)
|
||||
|
||||
pac.SetupBones(ent)
|
||||
pac.ResetBones(ent)
|
||||
|
||||
if ent.pac_can_legacy_scale ~= false then
|
||||
ent.pac_can_legacy_scale = not not ent.pac_can_legacy_scale
|
||||
end
|
||||
end
|
||||
|
||||
surface.CreateFont("pac_urlobj_loading",
|
||||
{
|
||||
font = "Arial",
|
||||
size = 20,
|
||||
weight = 10,
|
||||
antialias = true,
|
||||
outline = true,
|
||||
}
|
||||
)
|
||||
|
||||
-- ugh lol
|
||||
local function RealDrawModel(self, ent, pos, ang)
|
||||
if self.Mesh then
|
||||
ent:SetModelScale(0,0)
|
||||
ent:DrawModel()
|
||||
|
||||
local matrix = Matrix()
|
||||
|
||||
matrix:SetAngles(ang)
|
||||
matrix:SetTranslation(pos)
|
||||
|
||||
if ent.pac_model_scale then
|
||||
matrix:Scale(ent.pac_model_scale)
|
||||
else
|
||||
matrix:Scale(self.Scale * self.Size)
|
||||
end
|
||||
|
||||
cam_PushModelMatrix(matrix)
|
||||
self.Mesh:Draw()
|
||||
cam_PopModelMatrix()
|
||||
else
|
||||
ent:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local _self, _ent, _pos, _ang
|
||||
local _return_status = false
|
||||
|
||||
local function protected_real_draw_model()
|
||||
RealDrawModel(_self, _ent, _pos, _ang)
|
||||
end
|
||||
|
||||
local function protected_inner_draw_model()
|
||||
local mat = _self.MaterialOverride or _self.Materialm
|
||||
|
||||
render_MaterialOverride(mat)
|
||||
|
||||
if mat then
|
||||
render_SetMaterial(mat)
|
||||
end
|
||||
|
||||
pac.render_material = mat
|
||||
|
||||
-- Render model
|
||||
local passCount = math_max (1, _self.Passes)
|
||||
|
||||
if _self.Alpha >= 1 then
|
||||
passCount = math_min (passCount, 1)
|
||||
end
|
||||
|
||||
for _ = 1, passCount do
|
||||
local status = ProtectedCall(protected_real_draw_model)
|
||||
|
||||
if not status then
|
||||
_return_status = false
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
render_PushFlashlightMode(true)
|
||||
|
||||
ProtectedCall(protected_real_draw_model)
|
||||
|
||||
render_PopFlashlightMode()
|
||||
|
||||
_return_status = true
|
||||
end
|
||||
|
||||
function PART:DrawModel(ent, pos, ang)
|
||||
if self.Alpha == 0 or self.Size == 0 then return end
|
||||
|
||||
if self.loading_obj then
|
||||
self:DrawLoadingText(ent, pos, ang)
|
||||
end
|
||||
|
||||
if self.loading_obj and not self.Mesh then return end
|
||||
|
||||
local textureFilter = self.texfilter_enum or TEXFILTER.ANISOTROPIC
|
||||
local filter_updated = textureFilter ~= TEXFILTER.ANISOTROPIC or self.Mesh
|
||||
|
||||
if filter_updated then
|
||||
render_PushFilterMin(textureFilter)
|
||||
render_PushFilterMag(textureFilter)
|
||||
end
|
||||
|
||||
_self, _ent, _pos, _ang = self, ent, pos, ang
|
||||
|
||||
ProtectedCall(protected_inner_draw_model)
|
||||
|
||||
if filter_updated then
|
||||
render_PopFilterMag()
|
||||
render_PopFilterMin()
|
||||
end
|
||||
|
||||
-- Render "blur"
|
||||
if self.BlurLength > 0 and _return_status then
|
||||
self:DrawBlur(ent, pos, ang)
|
||||
end
|
||||
|
||||
render_MaterialOverride()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:DrawLoadingText(ent, pos, ang)
|
||||
cam.Start2D()
|
||||
cam.IgnoreZ(true)
|
||||
local pos2d = pos:ToScreen()
|
||||
|
||||
surface.SetFont("pac_urlobj_loading")
|
||||
surface.SetTextColor(255, 255, 255, 255)
|
||||
|
||||
local str = self.loading_obj .. string.rep(".", pac.RealTime * 3 % 3)
|
||||
local w, h = surface.GetTextSize(self.loading_obj .. "...")
|
||||
|
||||
surface.SetTextPos(pos2d.x - w / 2, pos2d.y - h / 2)
|
||||
surface.DrawText(str)
|
||||
cam.IgnoreZ(false)
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
function PART:DrawBlur(ent, pos, ang)
|
||||
if pac.drawing_motionblur_alpha then return end
|
||||
self.blur_history = self.blur_history or {}
|
||||
|
||||
local blurSpacing = self.BlurSpacing
|
||||
|
||||
if not self.blur_last_add or blurSpacing == 0 or self.blur_last_add < pac.RealTime then
|
||||
table_insert(self.blur_history, {pos, ang})
|
||||
self.blur_last_add = pac.RealTime + blurSpacing / 1000
|
||||
end
|
||||
|
||||
local blurHistoryLength = #self.blur_history
|
||||
for i = 1, blurHistoryLength do
|
||||
pos, ang = self.blur_history[i][1], self.blur_history[i][2]
|
||||
|
||||
render_SetBlend(self.Alpha * (i / blurHistoryLength))
|
||||
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
pac.SetupBones(ent)
|
||||
|
||||
RealDrawModel(self, ent, pos, ang)
|
||||
end
|
||||
|
||||
local maximumBlurHistoryLength = math.min(self.BlurLength, 20)
|
||||
while #self.blur_history >= maximumBlurHistoryLength do
|
||||
table_remove(self.blur_history, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function set_mesh(part, mesh)
|
||||
local owner = part:GetOwner()
|
||||
part.Mesh = mesh
|
||||
pac.ResetBoneCache(owner)
|
||||
|
||||
if not part.Materialm then
|
||||
part.Materialm = Material("error")
|
||||
end
|
||||
|
||||
function owner.pacDrawModel(ent, simple)
|
||||
if simple then
|
||||
RealDrawModel(part, ent, ent:GetPos(), ent:GetAngles())
|
||||
else
|
||||
part:ModifiersPreEvent("OnDraw")
|
||||
part:DrawModel(ent, ent:GetPos(), ent:GetAngles())
|
||||
part:ModifiersPostEvent("OnDraw")
|
||||
end
|
||||
end
|
||||
|
||||
-- temp
|
||||
owner:SetRenderBounds(Vector(1, 1, 1) * -300, Vector(1, 1, 1) * 300)
|
||||
end
|
||||
|
||||
do
|
||||
pac.urlobj = include("pac3/libraries/urlobj/urlobj.lua")
|
||||
|
||||
function PART:SetModel(modelPath)
|
||||
if modelPath:find("^mdlhttp") then
|
||||
self.Model = modelPath
|
||||
|
||||
modelPath = modelPath:gsub("^mdl", "")
|
||||
|
||||
pac.DownloadMDL(modelPath, function(path)
|
||||
if self:IsValid() and self:GetOwner():IsValid() then
|
||||
local ent = self:GetOwner()
|
||||
self.loading = nil
|
||||
pac.ResetBoneCache(ent)
|
||||
ent:SetModel(path)
|
||||
end
|
||||
end, function(err)
|
||||
pac.Message(err)
|
||||
if self:IsValid() and self:GetOwner():IsValid() then
|
||||
local ent = self:GetOwner()
|
||||
self.loading = nil
|
||||
pac.ResetBoneCache(ent)
|
||||
ent:SetModel("models/error.mdl")
|
||||
end
|
||||
end, self:GetPlayerOwner())
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if modelPath and modelPath:find("http") and pac.urlobj then
|
||||
self.loading_obj = "downloading"
|
||||
|
||||
if not self.is_obj then
|
||||
self:Initialize(true)
|
||||
end
|
||||
|
||||
pac.urlobj.GetObjFromURL(modelPath, false, false,
|
||||
function(meshes, err)
|
||||
if not self:IsValid() then return end
|
||||
|
||||
self.loading_obj = false
|
||||
|
||||
if not meshes and err then
|
||||
self:GetOwner():SetModel("models/error.mdl")
|
||||
self.Mesh = nil
|
||||
return
|
||||
end
|
||||
|
||||
if table.Count(meshes) == 1 then
|
||||
set_mesh(self, select(2, next(meshes)))
|
||||
else
|
||||
for key, mesh in pairs(meshes) do
|
||||
local part = pac.CreatePart("model", self:GetParentOwnerName())
|
||||
part:SetName(key)
|
||||
part:SetParent(self)
|
||||
part:SetMaterial(self:GetMaterial())
|
||||
set_mesh(part, mesh)
|
||||
end
|
||||
|
||||
self:SetAlpha(0)
|
||||
end
|
||||
end,
|
||||
function(finished, statusMessage)
|
||||
if finished then
|
||||
self.loading_obj = nil
|
||||
else
|
||||
self.loading_obj = statusMessage
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
self.Model = modelPath
|
||||
return
|
||||
end
|
||||
|
||||
if self.is_obj or not self.Owner:IsValid() then
|
||||
self:Initialize(false)
|
||||
end
|
||||
|
||||
self.Mesh = nil
|
||||
|
||||
local real_model = modelPath
|
||||
local ret = hook.Run("pac_model:SetModel", self, modelPath, self.ModelFallback)
|
||||
if ret == nil then
|
||||
real_model = pac.FilterInvalidModel(real_model,self.ModelFallback)
|
||||
else
|
||||
modelPath = ret or modelPath
|
||||
real_model = modelPath
|
||||
real_model = pac.FilterInvalidModel(real_model,self.ModelFallback)
|
||||
end
|
||||
|
||||
self.Model = modelPath
|
||||
pac.ResetBoneCache(self.Owner)
|
||||
self.Owner:SetModel(real_model)
|
||||
|
||||
if not self:IsHidden() and not self:IsDrawHidden() then
|
||||
-- notify children about model change
|
||||
self:ShowFromRendering()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local NORMAL = Vector(1,1,1)
|
||||
|
||||
function PART:CheckScale()
|
||||
-- RenderMultiply doesn't work with this..
|
||||
if (self.UseLegacyScale or self.BoneMerge) and self.Owner:IsValid() and self.Owner:GetBoneCount() and self.Owner:GetBoneCount() > 1 then
|
||||
if self.Scale * self.Size ~= NORMAL then
|
||||
if not self.requires_bone_model_scale then
|
||||
self.requires_bone_model_scale = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
self.requires_bone_model_scale = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetAlternativeScaling(b)
|
||||
self.AlternativeScaling = b
|
||||
self:SetScale(self.Scale)
|
||||
end
|
||||
|
||||
function PART:SetScale(var)
|
||||
var = var or Vector(1,1,1)
|
||||
|
||||
self.Scale = var
|
||||
|
||||
if self.AlternativeScaling then
|
||||
if not self:CheckScale() then
|
||||
pac.SetModelScale(self.Owner, self.Scale, nil, self.UseLegacyScale)
|
||||
self.used_alt_scale = true
|
||||
end
|
||||
else
|
||||
if self.used_alt_scale then
|
||||
pac.SetModelScale(self.Owner, nil, 1, self.UseLegacyScale)
|
||||
self.used_alt_scale = false
|
||||
end
|
||||
if not self:CheckScale() then
|
||||
pac.SetModelScale(self.Owner, self.Scale * self.Size, nil, self.UseLegacyScale)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSize(var)
|
||||
var = var or 1
|
||||
|
||||
self.Size = var
|
||||
|
||||
if self.AlternativeScaling then
|
||||
pac.SetModelScale(self.Owner, nil, self.Size, self.UseLegacyScale)
|
||||
self.used_alt_scale = true
|
||||
else
|
||||
if self.used_alt_scale then
|
||||
pac.SetModelScale(self.Owner, nil, 1, self.UseLegacyScale)
|
||||
self.used_alt_scale = false
|
||||
end
|
||||
if not self:CheckScale() then
|
||||
pac.SetModelScale(self.Owner, self.Scale * self.Size, nil, self.UseLegacyScale)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetBrightness(num)
|
||||
self.Brightness = num
|
||||
self:SetColor(self:GetColor())
|
||||
end
|
||||
|
||||
function PART:SetColor(var)
|
||||
self.Color = var
|
||||
local owner = self:GetPlayerOwner()
|
||||
|
||||
if self.UsePlayerColor and owner:IsValid() then
|
||||
local c = owner:GetPlayerColor() * self.Brightness
|
||||
self.Colorf = {c.x, c.y, c.z}
|
||||
elseif self.UseWeaponColor and owner:IsValid() then
|
||||
local c = owner:GetWeaponColor() * self.Brightness
|
||||
self.Colorf = {c.x, c.y, c.z}
|
||||
else
|
||||
self.Colorf = {(var.r / 255) * self.Brightness, (var.g / 255) * self.Brightness, (var.b / 255) * self.Brightness}
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetUseWeaponColor(b)
|
||||
self.UseWeaponColor = b
|
||||
self:SetColor(self:GetColor())
|
||||
end
|
||||
|
||||
function PART:SetUsePlayerColor(b)
|
||||
self.UsePlayerColor = b
|
||||
self:SetColor(self:GetColor())
|
||||
end
|
||||
|
||||
function PART:FixMaterial()
|
||||
local mat = self.Materialm
|
||||
|
||||
if not mat then return end
|
||||
|
||||
local shader = mat:GetShader()
|
||||
|
||||
if shader == "UnlitGeneric" then
|
||||
local tex_path = mat:GetString("$basetexture")
|
||||
|
||||
if tex_path then
|
||||
local params = {}
|
||||
|
||||
params["$basetexture"] = tex_path
|
||||
params["$vertexcolor"] = 1
|
||||
params["$additive"] = 1
|
||||
|
||||
self.Materialm = pac.CreateMaterial(pac.uid"pac_fixmat_", "VertexLitGeneric", params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var) then
|
||||
if var == "" then
|
||||
self.Materialm = nil
|
||||
else
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:FixMaterial()
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
self.Material = var
|
||||
end
|
||||
|
||||
function PART:SetSkin(var)
|
||||
var = var or 0
|
||||
|
||||
self.Skin = var
|
||||
self.Owner:SetSkin(var)
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
if not self.OwnerEntity then
|
||||
timer.Simple(0, function()
|
||||
SafeRemoveEntity(self.Owner)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetLodOverride(num)
|
||||
local ent = self.Owner
|
||||
if ent:IsValid() then
|
||||
ent:SetLOD(num)
|
||||
self.LodOverride = num
|
||||
end
|
||||
end
|
||||
|
||||
function PART:CheckBoneMerge()
|
||||
local ent = self.Owner
|
||||
|
||||
if self.skip_orient then return end
|
||||
|
||||
if ent:IsValid() and not ent:IsPlayer() then
|
||||
if self.BoneMerge then
|
||||
local owner = self:GetParentOwner()
|
||||
|
||||
if ent:GetParent() ~= owner then
|
||||
ent:SetParent(owner)
|
||||
|
||||
if not ent:IsEffectActive(EF_BONEMERGE) then
|
||||
ent:AddEffects(EF_BONEMERGE)
|
||||
end
|
||||
end
|
||||
else
|
||||
if ent:GetParent():IsValid() then
|
||||
ent:SetParent(NULL)
|
||||
|
||||
if ent:IsEffectActive(EF_BONEMERGE) then
|
||||
ent:RemoveEffects(EF_BONEMERGE)
|
||||
end
|
||||
|
||||
self.requires_bone_model_scale = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local SCALE_NORMAL = Vector(1, 1, 1)
|
||||
function PART:OnBuildBonePositions()
|
||||
if self.AlternativeScaling then return end
|
||||
|
||||
local ent = self:GetOwner()
|
||||
local owner = self:GetParentOwner()
|
||||
|
||||
if not ent:IsValid() or not owner:IsValid() or not ent:GetBoneCount() or ent:GetBoneCount() < 1 then return end
|
||||
|
||||
if self.requires_bone_model_scale then
|
||||
local scale = self.Scale * self.Size
|
||||
|
||||
for i = 0, ent:GetBoneCount() - 1 do
|
||||
if i == 0 then
|
||||
ent:ManipulateBoneScale(i, ent:GetManipulateBoneScale(i) * Vector(scale.x ^ 0.25, scale.y ^ 0.25, scale.z ^ 0.25))
|
||||
else
|
||||
ent:ManipulateBonePosition(i, ent:GetManipulateBonePosition(i) + Vector((scale.x-1) ^ 4, 0, 0))
|
||||
ent:ManipulateBoneScale(i, ent:GetManipulateBoneScale(i) * scale)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
266
lua/pac3/core/client/parts/legacy/ogg.lua
Normal file
266
lua/pac3/core/client/parts/legacy/ogg.lua
Normal file
@@ -0,0 +1,266 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
include("pac3/libraries/webaudio/urlogg.lua")
|
||||
include("pac3/libraries/webaudio/browser.lua")
|
||||
include("pac3/libraries/webaudio/stream.lua")
|
||||
include("pac3/libraries/webaudio/streams.lua")
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "legacy ogg"
|
||||
PART.ClassName = "ogg"
|
||||
|
||||
PART.Group = "legacy"
|
||||
PART.Icon = 'icon16/music.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("URL", "")
|
||||
BUILDER:GetSet("Volume", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Pitch", 1, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("Radius", 1500)
|
||||
BUILDER:GetSet("PlayCount", 1, {editor_onchange = function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Round(math.max(num, 0))
|
||||
end})
|
||||
BUILDER:GetSet("Doppler", false)
|
||||
BUILDER:GetSet("StopOnHide", false)
|
||||
BUILDER:GetSet("PauseOnHide", false)
|
||||
BUILDER:GetSet("Overlapping", false)
|
||||
|
||||
BUILDER:GetSet("FilterType", 0, {editor_onchange = function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Round(math.Clamp(num, 0, 2))
|
||||
end})
|
||||
BUILDER:GetSet("FilterFraction", 1, {editor_sensitivity = 0.125, editor_clamp = {0, 1}})
|
||||
|
||||
--BUILDER:GetSet("Echo", false)
|
||||
--BUILDER:GetSet("EchoDelay", 0.5)
|
||||
--BUILDER:GetSet("EchoFeedback", 0.75)
|
||||
|
||||
BUILDER:GetSet("PlayOnFootstep", false)
|
||||
BUILDER:GetSet("MinPitch", 0, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("MaxPitch", 0, {editor_sensitivity = 0.125})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:Initialize()
|
||||
self.streams = {}
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName("/".. self:GetURL())
|
||||
return str and str:match(".+/(.-)%.") or "no sound"
|
||||
end
|
||||
|
||||
local stream_vars = {"Doppler", "Radius"}
|
||||
|
||||
local BIND = function(propertyName, setterMethodName, check)
|
||||
table.insert(stream_vars, propertyName)
|
||||
setterMethodName = setterMethodName or "Set" .. propertyName
|
||||
PART["Set" .. propertyName] = function(self, value)
|
||||
if check then
|
||||
value = check(value)
|
||||
end
|
||||
|
||||
for url, stream in pairs(self.streams) do
|
||||
if stream:IsValid() then
|
||||
stream[setterMethodName](stream, value)
|
||||
else
|
||||
self.streams[url] = nil
|
||||
end
|
||||
end
|
||||
|
||||
self[propertyName] = value
|
||||
end
|
||||
end
|
||||
|
||||
BIND("Pitch", "SetPlaybackSpeed")
|
||||
BIND("PlayCount", "SetMaxLoopCount" )
|
||||
BIND("Volume", nil, function(n) return math.Clamp(n, 0, 4) end)
|
||||
BIND("Radius", "SetSourceRadius" )
|
||||
|
||||
BIND("FilterType")
|
||||
BIND("FilterFraction")
|
||||
|
||||
--BIND("Echo")
|
||||
--BIND("EchoDelay")
|
||||
--BIND("EchoFeedback", nil, function(n) return math.Clamp(n, 0, 0.99) end)
|
||||
|
||||
function PART:OnThink()
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
|
||||
for url, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[url] = nil goto CONTINUE end
|
||||
|
||||
if self.PlayCount == 0 then
|
||||
stream:Resume()
|
||||
end
|
||||
|
||||
if stream.owner_set ~= owner and owner:IsValid() then
|
||||
stream:SetSourceEntity(owner, true)
|
||||
stream.owner_set = owner
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
if self.last_playonfootstep ~= self.PlayOnFootstep then
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() and ent:IsPlayer() then
|
||||
ent.pac_footstep_override = ent.pac_footstep_override or {}
|
||||
|
||||
if self.PlayOnFootstep then
|
||||
ent.pac_footstep_override[self.UniqueID] = self
|
||||
else
|
||||
ent.pac_footstep_override[self.UniqueID] = nil
|
||||
end
|
||||
|
||||
if table.Count(ent.pac_footstep_override) == 0 then
|
||||
ent.pac_footstep_override = nil
|
||||
end
|
||||
|
||||
self.last_playonfootstep = self.PlayOnFootstep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetURL(URL)
|
||||
|
||||
local urls = {}
|
||||
|
||||
for _, url in pairs(URL:Split(";")) do
|
||||
local min, max = url:match(".+%[(.-),(.-)%]")
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
|
||||
if min and max then
|
||||
for i = min, max do
|
||||
table.insert(urls, (url:gsub("%[.-%]", i)))
|
||||
end
|
||||
else
|
||||
table.insert(urls, url)
|
||||
end
|
||||
end
|
||||
|
||||
for _, stream in pairs(self.streams) do
|
||||
if stream:IsValid() then
|
||||
stream:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
self.streams = {}
|
||||
|
||||
self:SetError()
|
||||
|
||||
for _, url in pairs(urls) do
|
||||
local stream = pac.webaudio.Streams.CreateStream(url)
|
||||
self.streams[url] = stream
|
||||
|
||||
stream:Enable3D(true)
|
||||
stream.OnLoad = function()
|
||||
for _, key in ipairs(stream_vars) do
|
||||
self["Set" .. key](self, self["Get" .. key](self))
|
||||
end
|
||||
end
|
||||
stream.OnError = function(err, info)
|
||||
pac.Message("OGG error: ", err, " reason: ", info or "none")
|
||||
self:SetError(str)
|
||||
end
|
||||
|
||||
if pace and pace.Editor:IsValid() and pace.current_part:IsValid() and pace.current_part.ClassName == "ogg" and self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
stream:Play()
|
||||
end
|
||||
end
|
||||
|
||||
self.URL = URL
|
||||
end
|
||||
|
||||
PART.last_stream = NULL
|
||||
|
||||
function PART:PlaySound(_, additiveVolumeFraction)
|
||||
additiveVolumeFraction = additiveVolumeFraction or 0
|
||||
local stream = table.Random(self.streams) or NULL
|
||||
|
||||
if pac.webaudio.sample_rate and pac.webaudio.sample_rate > 48000 then
|
||||
pac.Message(Color(255, 0, 0), "The ogg part (custom sounds) might not work because you have your sample rate set to ", pac.webaudio.sample_rate, " Hz. Set it to 48000 or below if you experience any issues.")
|
||||
self:SetInfo("The ogg part (custom sounds) might not work because you have your sample rate set to " .. pac.webaudio.sample_rate .. " Hz. Set it to 48000 or below if you experience any issues.")
|
||||
end
|
||||
|
||||
if not stream:IsValid() then return end
|
||||
|
||||
stream:SetAdditiveVolumeModifier (additiveVolumeFraction)
|
||||
|
||||
if self.last_stream:IsValid() and not self.Overlapping then
|
||||
self.last_stream:Stop()
|
||||
end
|
||||
|
||||
if self.MinPitch ~= self.MaxPitch then
|
||||
stream:SetAdditivePitchModifier(math.Rand(self.MinPitch, self.MaxPitch))
|
||||
else
|
||||
stream:SetAdditivePitchModifier(0)
|
||||
end
|
||||
|
||||
if self.PauseOnHide then
|
||||
stream:Resume()
|
||||
else
|
||||
stream:Start()
|
||||
end
|
||||
|
||||
self.last_stream = stream
|
||||
end
|
||||
|
||||
function PART:StopSound()
|
||||
for key, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[key] = nil goto CONTINUE end
|
||||
|
||||
if not self.StopOnHide then
|
||||
if self.PauseOnHide then
|
||||
stream:Pause()
|
||||
else
|
||||
stream:Stop()
|
||||
end
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
self:PlaySound()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self:StopSound()
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
for key, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[key] = nil goto CONTINUE end
|
||||
|
||||
stream:Remove()
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetDoppler(num)
|
||||
for key, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[key] = nil goto CONTINUE end
|
||||
|
||||
stream:EnableDoppler(num)
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
self.Doppler = num
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
320
lua/pac3/core/client/parts/legacy/sound.lua
Normal file
320
lua/pac3/core/client/parts/legacy/sound.lua
Normal file
@@ -0,0 +1,320 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "legacy sound"
|
||||
PART.Group = "legacy"
|
||||
PART.ClassName = "sound"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/sound.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:GetSet("Sound", "")
|
||||
BUILDER:GetSet("Volume", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Pitch", 0.4, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("MinPitch", 100, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("MaxPitch", 100, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("RootOwner", true)
|
||||
BUILDER:GetSet("SoundLevel", 100)
|
||||
BUILDER:GetSet("LocalPlayerOnly", false)
|
||||
BUILDER:SetPropertyGroup("playback")
|
||||
BUILDER:GetSet("PlayOnFootstep", false)
|
||||
BUILDER:GetSet("Overlapping", false)
|
||||
BUILDER:GetSet("Loop", false)
|
||||
BUILDER:GetSet("Sequential", false, {description = "if there are multiple sounds (separated by ; or using [min,max] notation), plays these sounds in sequential order instead of randomly"})
|
||||
BUILDER:GetSet("SequentialStep",1,
|
||||
{editor_onchange =
|
||||
function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Round(num)
|
||||
end})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName("/" .. self:GetSound())
|
||||
return str and str:match(".+/(.-)%.") or "no sound"
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
--self:PlaySound()
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
self.played_overlapping = false
|
||||
self:PlaySound()
|
||||
end
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent:IsPlayer() then
|
||||
ent.pac_footstep_override = ent.pac_footstep_override or {}
|
||||
if self.PlayOnFootstep then
|
||||
ent.pac_footstep_override[self.UniqueID] = self
|
||||
else
|
||||
ent.pac_footstep_override[self.UniqueID] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self.played_overlapping = false
|
||||
self:StopSound()
|
||||
|
||||
if self.PlayOnFootstep then
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.pac_footstep_override = nil
|
||||
|
||||
if ent:IsPlayer() then
|
||||
ent.pac_footstep_override = ent.pac_footstep_override or {}
|
||||
|
||||
|
||||
ent.pac_footstep_override[self.UniqueID] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
if not self.csptch then
|
||||
self:PlaySound()
|
||||
else
|
||||
if self.Loop then
|
||||
pac.playing_sound = true
|
||||
if not self.csptch:IsPlaying() then self.csptch:Play() end
|
||||
self.csptch:ChangePitch((self.Pitch * 255) + math.sin(pac.RealTime) / 2, 0)
|
||||
pac.playing_sound = false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- fixes by Python 1320
|
||||
|
||||
-- Sound protection. TODO: MAKE A BETTER FIX. Iterative removal of the special characters?
|
||||
-- This will make special sound effects break if used directly from lua, but I doubt this is common.
|
||||
-- Won't protect engine but engine shouldn't do this anyway.
|
||||
-- TODO: Beta new sound functions
|
||||
|
||||
-- https://developer.valvesoftware.com/wiki/Soundscripts#Sound_Characters
|
||||
-- we are using this for bad replacements as it won't break stuff too badly ["*"]=true,
|
||||
|
||||
local bad =
|
||||
{
|
||||
["#"] = true,
|
||||
["@"] = true,
|
||||
[">"] = true,
|
||||
["<"] = true,
|
||||
["^"] = true,
|
||||
[")"] = true,
|
||||
["}"] = true,
|
||||
["$"] = true,
|
||||
["!"] = true,
|
||||
["?"] = true, -- especially bad
|
||||
}
|
||||
|
||||
local function fix(snd)
|
||||
if bad[snd:sub(1,1)] then
|
||||
snd = snd:gsub("^(.)",function() return "*" end)
|
||||
end
|
||||
if bad[snd:sub(2,2)] then
|
||||
snd = snd:gsub("^(..)",function(a) return a[1] .. "*" end)
|
||||
end
|
||||
return snd
|
||||
end
|
||||
|
||||
function PART:SetSound(str)
|
||||
if not isstring(str) then self.Sound = "" return end
|
||||
|
||||
if bad[str:sub(1,1)] or bad[str:sub(2,2)] then
|
||||
str = fix(str)
|
||||
end
|
||||
|
||||
self.Sound = str:gsub("\\", "/")
|
||||
|
||||
self:PlaySound()
|
||||
end
|
||||
|
||||
function PART:SetVolume(num)
|
||||
self.Volume = num
|
||||
|
||||
if not self.csptch then
|
||||
self:PlaySound()
|
||||
end
|
||||
|
||||
if self.csptch then
|
||||
self.csptch:ChangeVolume(math.Clamp(self.Volume, 0.001, 1), 0)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetPitch(num)
|
||||
self.Pitch = num
|
||||
|
||||
if not self.csptch then
|
||||
self:PlaySound()
|
||||
end
|
||||
|
||||
if self.csptch then
|
||||
self.csptch:ChangePitch(math.Clamp(self.Pitch * 255, 1, 255), 0)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PlaySound(osnd, ovol)
|
||||
local ent = self.RootOwner and self:GetRootPart():GetOwner() or self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if ent:GetClass() == "viewmodel" or ent == pac.LocalHands then
|
||||
ent = pac.LocalPlayer
|
||||
end
|
||||
|
||||
if self:GetLocalPlayerOnly() and ent ~= pac.LocalPlayer then return end
|
||||
|
||||
local snd
|
||||
|
||||
if osnd and self.Sound == "" then
|
||||
snd = osnd
|
||||
else
|
||||
local sounds = self.Sound:Split(";")
|
||||
|
||||
--case 1: proper semicolon list
|
||||
if #sounds > 1 then
|
||||
if self.Sequential then
|
||||
self.seq_index = self.seq_index or 1
|
||||
|
||||
snd = sounds[self.seq_index]
|
||||
|
||||
self.seq_index = self.seq_index + self.SequentialStep
|
||||
self.seq_index = self.seq_index % (#sounds+1)
|
||||
if self.seq_index == 0 then self.seq_index = 1 end
|
||||
else snd = table.Random(sounds) end
|
||||
|
||||
--case 2: one sound, which may or may not be bracket notation
|
||||
elseif #sounds == 1 then
|
||||
--bracket notation
|
||||
if string.match(sounds[1],"%[(%d-),(%d-)%]") then
|
||||
local function minmaxpath(minmax,str)
|
||||
local min, max = minmax:match("%[(%d-),(%d-)%]")
|
||||
if minmax:match("%[(%d-),(%d-)%]") == nil then return 1 end
|
||||
if max < min then
|
||||
max = min
|
||||
end
|
||||
if str == "min" then return tonumber(min)
|
||||
elseif str == "max" then return tonumber(max) else return tonumber(max) end
|
||||
end
|
||||
if self.Sequential then
|
||||
self.seq_index = self.seq_index or minmaxpath(self.Sound,"min")
|
||||
snd = self.Sound:gsub(
|
||||
"(%[%d-,%d-%])",self.seq_index
|
||||
)
|
||||
self.seq_index = self.seq_index + self.SequentialStep
|
||||
|
||||
local span = minmaxpath(self.Sound,"max") - minmaxpath(self.Sound,"min") + 1
|
||||
if self.seq_index > minmaxpath(self.Sound,"max") then
|
||||
self.seq_index = self.seq_index - span
|
||||
elseif self.seq_index < minmaxpath(self.Sound,"min") then
|
||||
self.seq_index = self.seq_index + span
|
||||
end
|
||||
else
|
||||
snd = self.Sound:gsub(
|
||||
"(%[%d-,%d-%])",
|
||||
function(minmax)
|
||||
local min, max = minmax:match("%[(%d-),(%d-)%]")
|
||||
if max < min then
|
||||
max = min
|
||||
end
|
||||
return math.random(min, max)
|
||||
end
|
||||
)
|
||||
end
|
||||
--single sound
|
||||
else snd = sounds[1] or osnd end
|
||||
end
|
||||
end
|
||||
|
||||
local vol
|
||||
|
||||
if osnd and self.Volume == -1 then
|
||||
vol = ovol or 1
|
||||
else
|
||||
vol = self.Volume
|
||||
end
|
||||
|
||||
local pitch
|
||||
|
||||
if self.MinPitch == self.MaxPitch then
|
||||
pitch = self.Pitch * 255
|
||||
else
|
||||
pitch = math.random(self.MinPitch, self.MaxPitch)
|
||||
end
|
||||
|
||||
pac.playing_sound = true
|
||||
|
||||
if self.Overlapping then
|
||||
if not self.played_overlapping then
|
||||
ent:EmitSound(snd, vol * 160, pitch)
|
||||
self.played_overlapping = true
|
||||
end
|
||||
else
|
||||
if self.csptch then
|
||||
self.csptch:Stop()
|
||||
end
|
||||
|
||||
local csptch = CreateSound(ent, snd)
|
||||
|
||||
|
||||
csptch:SetSoundLevel(self.SoundLevel)
|
||||
csptch:PlayEx(vol, pitch)
|
||||
ent.pac_csptch = csptch
|
||||
self.csptch = csptch
|
||||
end
|
||||
|
||||
pac.playing_sound = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:StopSound()
|
||||
if self.csptch then
|
||||
self.csptch:Stop()
|
||||
end
|
||||
end
|
||||
|
||||
local channels =
|
||||
{
|
||||
CHAN_AUTO = 0,
|
||||
CHAN_WEAPON = 1,
|
||||
CHAN_VOICE = 2,
|
||||
CHAN_ITEM = 3,
|
||||
CHAN_BODY = 4,
|
||||
CHAN_STREAM = 5,
|
||||
CHAN_STATIC = 6,
|
||||
}
|
||||
|
||||
for key, CHAN in pairs(channels) do
|
||||
sound.Add(
|
||||
{
|
||||
name = "pac_silence_" .. key:lower(),
|
||||
channel = CHAN,
|
||||
volume = 0,
|
||||
soundlevel = 0,
|
||||
pitchstart = 0,
|
||||
pitchend = 0,
|
||||
sound = "ambient/_period.wav"
|
||||
} )
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
186
lua/pac3/core/client/parts/legacy/trail.lua
Normal file
186
lua/pac3/core/client/parts/legacy/trail.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
--[[
|
||||
| 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 Lerp = Lerp
|
||||
local tonumber = tonumber
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
local math_ceil = math.ceil
|
||||
local math_abs = math.abs
|
||||
local render_StartBeam = render.StartBeam
|
||||
local cam_IgnoreZ = cam.IgnoreZ
|
||||
local render_EndBeam = render.EndBeam
|
||||
local render_AddBeam = render.AddBeam
|
||||
local render_SetMaterial = render.SetMaterial
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "legacy trail"
|
||||
PART.ClassName = "trail"
|
||||
PART.Group = "legacy"
|
||||
|
||||
PART.Icon = 'icon16/arrow_undo.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("TrailPath", "trails/laser", {editor_panel = "material"})
|
||||
BUILDER:GetSet("StartSize", 3)
|
||||
BUILDER:GetSet("EndSize", 0)
|
||||
BUILDER:GetSet("Length", 100)
|
||||
BUILDER:GetSet("Spacing", 1)
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("StartColor", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("EndColor", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("StartAlpha", 1)
|
||||
BUILDER:GetSet("EndAlpha", 1)
|
||||
BUILDER:PropertyOrder("Translucent")
|
||||
BUILDER:GetSet("Stretch", false)
|
||||
BUILDER:SetPropertyGroup("other")
|
||||
BUILDER:PropertyOrder("DrawOrder")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName("/" .. self:GetTrailPath())
|
||||
local matched = str and str:match(".+/(.+)")
|
||||
return matched and matched:gsub("%..+", "") or "error"
|
||||
end
|
||||
|
||||
PART.LastAdd = 0
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetTrailPath(self.TrailPath)
|
||||
|
||||
self.StartColorC = Color(255, 255, 255, 255)
|
||||
self.EndColorC = Color(255, 255, 255, 255)
|
||||
end
|
||||
|
||||
function PART:SetStartColor(v)
|
||||
self.StartColorC = self.StartColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.StartColorC.r = v.r
|
||||
self.StartColorC.g = v.g
|
||||
self.StartColorC.b = v.b
|
||||
|
||||
self.StartColor = v
|
||||
end
|
||||
|
||||
function PART:SetEndColor(v)
|
||||
self.EndColorC = self.EndColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.EndColorC.r = v.r
|
||||
self.EndColorC.g = v.g
|
||||
self.EndColorC.b = v.b
|
||||
|
||||
self.EndColor = v
|
||||
end
|
||||
|
||||
function PART:SetStartAlpha(n)
|
||||
self.StartColorC = self.StartColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.StartColorC.a = n * 255
|
||||
|
||||
self.StartAlpha = n
|
||||
end
|
||||
|
||||
function PART:SetEndAlpha(n)
|
||||
self.EndColorC = self.EndColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.EndColorC.a = n * 255
|
||||
|
||||
self.EndAlpha = n
|
||||
end
|
||||
|
||||
function PART:SetTrailPath(var)
|
||||
self.TrailPath = var
|
||||
self:SetMaterial(var)
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var) then
|
||||
if isstring(var) then
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
elseif type(var) == "IMaterial" then
|
||||
self.Materialm = var
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
if self.Materialm then
|
||||
local shader = self.Materialm:GetShader()
|
||||
if shader == "VertexLitGeneric" or shader == "Cable" or shader == "LightmappedGeneric" then
|
||||
self.Materialm = pac.MakeMaterialUnlitGeneric(self.Materialm, self.Id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self.points = {}
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self.points = {}
|
||||
end
|
||||
|
||||
local temp_color = Color(255, 255, 255)
|
||||
|
||||
function PART:OnDraw()
|
||||
local mat = self.MaterialOverride or self.Materialm
|
||||
|
||||
if mat and self.StartColorC and self.EndColorC then
|
||||
self.points = self.points or {}
|
||||
|
||||
local len = tonumber(self.Length)
|
||||
local spc = tonumber(self.Spacing)
|
||||
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
if spc == 0 or self.LastAdd < pac.RealTime then
|
||||
table_insert(self.points, pos)
|
||||
self.LastAdd = pac.RealTime + spc / 1000
|
||||
end
|
||||
|
||||
local count = #self.points
|
||||
|
||||
if spc > 0 then
|
||||
len = math_ceil(math_abs(len - spc))
|
||||
end
|
||||
|
||||
render_SetMaterial(mat)
|
||||
|
||||
render_StartBeam(count)
|
||||
for k, v in pairs(self.points) do
|
||||
local width = k / (len / self.StartSize)
|
||||
|
||||
local coord = (1 / count) * (k - 1)
|
||||
|
||||
temp_color.r = Lerp(coord, self.EndColorC.r, self.StartColorC.r)
|
||||
temp_color.g = Lerp(coord, self.EndColorC.g, self.StartColorC.g)
|
||||
temp_color.b = Lerp(coord, self.EndColorC.b, self.StartColorC.b)
|
||||
temp_color.a = Lerp(coord, self.EndColorC.a, self.StartColorC.a)
|
||||
|
||||
render_AddBeam(k == count and pos or v, width + self.EndSize, self.Stretch and coord or width, temp_color)
|
||||
end
|
||||
render_EndBeam()
|
||||
|
||||
if count >= len then
|
||||
table_remove(self.points, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
330
lua/pac3/core/client/parts/legacy/webaudio.lua
Normal file
330
lua/pac3/core/client/parts/legacy/webaudio.lua
Normal file
@@ -0,0 +1,330 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
local snd_mute_losefocus = GetConVar('snd_mute_losefocus')
|
||||
|
||||
PART.FriendlyName = "legacy webaudio"
|
||||
PART.ClassName = "webaudio"
|
||||
PART.Group = 'legacy'
|
||||
PART.Icon = 'icon16/sound_add.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("URL", "")
|
||||
BUILDER:GetSet("Volume", 1, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("Pitch", 1, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("MinimumRadius", 0)
|
||||
BUILDER:GetSet("MaximumRadius", 0)
|
||||
|
||||
BUILDER:GetSet("InnerAngle", 360)
|
||||
BUILDER:GetSet("OuterAngle", 360)
|
||||
BUILDER:GetSet("OuterVolume", 0)
|
||||
|
||||
BUILDER:GetSet("Loop", false)
|
||||
BUILDER:GetSet("StopOnHide", true)
|
||||
BUILDER:GetSet("PauseOnHide", false)
|
||||
BUILDER:GetSet("Overlapping", false)
|
||||
|
||||
BUILDER:GetSet("PlayOnFootstep", false)
|
||||
BUILDER:GetSet("RandomPitch", 0, {editor_sensitivity = 0.125})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:Initialize()
|
||||
self.streams = {}
|
||||
self.stream_count = 0
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName("/" .. self:GetURL())
|
||||
return str and str:match(".+/(.-)%.") or "no sound"
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
if self.last_playonfootstep ~= self.PlayOnFootstep then
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() and ent:IsPlayer() then
|
||||
ent.pac_footstep_override = ent.pac_footstep_override or {}
|
||||
|
||||
if self.PlayOnFootstep then
|
||||
ent.pac_footstep_override[self.UniqueID] = self
|
||||
else
|
||||
ent.pac_footstep_override[self.UniqueID] = nil
|
||||
end
|
||||
|
||||
if table.Count(ent.pac_footstep_override) == 0 then
|
||||
ent.pac_footstep_override = nil
|
||||
end
|
||||
|
||||
self.last_playonfootstep = self.PlayOnFootstep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if not self.streams then return end
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
local forward = ang:Forward()
|
||||
|
||||
local shouldMute = snd_mute_losefocus:GetBool()
|
||||
local focus = system.HasFocus()
|
||||
local volume = shouldMute and not focus and 0 or self:GetVolume()
|
||||
|
||||
for url, streamdata in pairs(self.streams) do
|
||||
local stream = streamdata.stream
|
||||
if streamdata.Loading then goto CONTINUE end
|
||||
|
||||
if not stream:IsValid() then
|
||||
self.streams[url] = nil
|
||||
self.stream_count = self.stream_count - 1
|
||||
-- TODO: Notify the user somehow or reload streams
|
||||
goto CONTINUE
|
||||
end
|
||||
|
||||
stream:SetPos(pos, forward)
|
||||
|
||||
if not self.random_pitch then self:SetRandomPitch(self.RandomPitch) end
|
||||
|
||||
stream:Set3DFadeDistance(self.MinimumRadius, self.MaximumRadius)
|
||||
stream:Set3DCone(self.InnerAngle, self.OuterAngle, self.OuterVolume)
|
||||
stream:SetVolume(volume)
|
||||
stream:SetPlaybackRate(self:GetPitch() + self.random_pitch)
|
||||
|
||||
if streamdata.StartPlaying then
|
||||
stream:Play()
|
||||
streamdata.StartPlaying = false
|
||||
end
|
||||
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetRandomPitch(num)
|
||||
self.RandomPitch = num
|
||||
self.random_pitch = math.Rand(-num, num)
|
||||
end
|
||||
|
||||
function PART:SetLoop(b)
|
||||
self.Loop = b
|
||||
self:SetURL(self:GetURL())
|
||||
end
|
||||
|
||||
function PART:SetURL(URL)
|
||||
self.InSetup = true
|
||||
|
||||
timer.Create("pac3_webaudio_seturl_" .. tostring(self), 0.5, 1, function()
|
||||
if not self:IsValid() then return end
|
||||
|
||||
self.InSetup = false
|
||||
self:SetupURLStreamsNow(URL)
|
||||
|
||||
if self.PlayAfterSetup then
|
||||
self:PlaySound()
|
||||
self.PlayAfterSetup = false
|
||||
end
|
||||
end)
|
||||
|
||||
self.URL = URL
|
||||
end
|
||||
|
||||
function PART:SetupURLStreamsNow(URL)
|
||||
local urls = {}
|
||||
|
||||
for _, url in ipairs(URL:Split(";")) do
|
||||
local min, max = url:match(".+%[(.-),(.-)%]")
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
|
||||
if min and max then
|
||||
for i = min, max do
|
||||
table.insert(urls, (url:gsub("%[.-%]", i)))
|
||||
end
|
||||
else
|
||||
table.insert(urls, url)
|
||||
end
|
||||
end
|
||||
|
||||
if #urls >= 150 then
|
||||
local cp = {}
|
||||
|
||||
for i = 1, 150 do
|
||||
table.insert(cp, urls[i])
|
||||
end
|
||||
|
||||
urls = cp
|
||||
end
|
||||
|
||||
for url, streamdata in pairs(self.streams) do
|
||||
if IsValid(streamdata.stream) then
|
||||
streamdata.stream:Stop()
|
||||
end
|
||||
end
|
||||
|
||||
self.streams = {}
|
||||
self.stream_count = 0
|
||||
|
||||
local inPace = pace and pace.IsActive() and pace.current_part == self and self:GetPlayerOwner() == pac.LocalPlayer
|
||||
|
||||
for _, url in ipairs(urls) do
|
||||
local flags = "3d noplay noblock"
|
||||
local function callback(channel, errorCode, errorString)
|
||||
local streamdata = self.streams[url]
|
||||
if not streamdata then
|
||||
if IsValid(channel) then channel:Stop() end
|
||||
return
|
||||
end
|
||||
|
||||
if not channel or not channel:IsValid() then
|
||||
pac.Message("Failed to load ", url, " (" .. flags .. ") - " .. (errorString or errorCode or "UNKNOWN"))
|
||||
self:SetError("Failed to load " .. url .. " (" .. flags .. ") - " .. (errorString or errorCode or "UNKNOWN"))
|
||||
|
||||
if errorCode == -1 then
|
||||
local msg = 'GMOD BUG: WAVe and Vorbis files are known to be not working with 3D flag, recode file into MPEG-3 format!'
|
||||
pac.Message(msg)
|
||||
self:SetError(msg)
|
||||
end
|
||||
|
||||
self.streams[url] = nil
|
||||
self.stream_count = self.stream_count - 1
|
||||
return
|
||||
end
|
||||
|
||||
streamdata.Loading = false
|
||||
|
||||
if streamdata.valid then
|
||||
if streamdata.PlayAfterLoad or inPace then
|
||||
channel:EnableLooping(self.Loop and channel:GetLength() > 0)
|
||||
streamdata.PlayAfterLoad = false
|
||||
streamdata.StartPlaying = true
|
||||
end
|
||||
|
||||
streamdata.stream = channel
|
||||
|
||||
if self.NeedsToPlayAfterLoad then
|
||||
self.NeedsToPlayAfterLoad = false
|
||||
self:PlaySound()
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
channel:Stop()
|
||||
end
|
||||
|
||||
self.streams[url] = {Loading = true, valid = true}
|
||||
self.stream_count = self.stream_count + 1
|
||||
|
||||
sound.PlayURL(url, flags, callback)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PlaySound()
|
||||
if self.InSetup then
|
||||
self.PlayAfterSetup = true
|
||||
return
|
||||
end
|
||||
|
||||
if self.stream_count == 0 then return end
|
||||
|
||||
local i, streamdata, atLeastSome = 0
|
||||
|
||||
while i < 20 and i < self.stream_count do
|
||||
streamdata = table.Random(self.streams)
|
||||
|
||||
if IsValid(streamdata.stream) then
|
||||
if not atLeastSome and streamdata.Loading then
|
||||
atLeastSome = true
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
local stream = streamdata.stream
|
||||
|
||||
if not IsValid(stream) then
|
||||
if atLeastSome then
|
||||
self.NeedsToPlayAfterLoad = true
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
self:SetRandomPitch(self.RandomPitch)
|
||||
|
||||
if IsValid(self.last_stream) and not self.Overlapping and self.last_stream ~= stream then
|
||||
pcall(function()
|
||||
-- this is erroring with noblock flag not being set, but we are doing that
|
||||
-- maybe the stream could be partially invalid
|
||||
self.last_stream:SetTime(0)
|
||||
self.last_stream:Pause()
|
||||
end)
|
||||
end
|
||||
|
||||
streamdata.StartPlaying = true
|
||||
self.last_stream = stream
|
||||
end
|
||||
|
||||
function PART:StopSound()
|
||||
local toremove
|
||||
|
||||
for key, streamdata in pairs(self.streams) do
|
||||
streamdata.PlayAfterLoad = false
|
||||
streamdata.StartPlaying = false
|
||||
|
||||
local stream = streamdata.stream
|
||||
|
||||
if IsValid(stream) then
|
||||
if self.PauseOnHide then
|
||||
stream:Pause()
|
||||
else
|
||||
pcall(function() stream:SetTime(0) end)
|
||||
stream:Pause()
|
||||
end
|
||||
elseif stream then
|
||||
toremove = toremove or {}
|
||||
table.insert(toremove, key)
|
||||
end
|
||||
end
|
||||
|
||||
if toremove then
|
||||
for i, index in ipairs(toremove) do
|
||||
self.streams[index] = nil
|
||||
end
|
||||
|
||||
self.stream_count = self.stream_count - #toremove
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
self:PlaySound()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
if self.StopOnHide then
|
||||
self:StopSound()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
for key, streamdata in pairs(self.streams) do
|
||||
streamdata.valid = false
|
||||
|
||||
if IsValid(streamdata.stream) then
|
||||
streamdata.stream:Stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
123
lua/pac3/core/client/parts/light.lua
Normal file
123
lua/pac3/core/client/parts/light.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
--[[
|
||||
| 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 DynamicLight = DynamicLight
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "light"
|
||||
PART.ClassName = "light2"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/lightbulb.png'
|
||||
PART.ProperColorRange = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("InnerAngle", 0)
|
||||
BUILDER:GetSet("OuterAngle", 0)
|
||||
BUILDER:GetSet("NoModel", false)
|
||||
BUILDER:GetSet("NoWorld", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Brightness", 8)
|
||||
BUILDER:GetSet("Size", 100, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Color", Vector(1, 1, 1), {editor_panel = "color2"})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetLight()
|
||||
if not self.light then
|
||||
self.light = DynamicLight(tonumber(self:GetPrintUniqueID(), 16))
|
||||
end
|
||||
self.light.decay = 0
|
||||
self.light.dietime = math.huge
|
||||
|
||||
return self.light
|
||||
end
|
||||
|
||||
function PART:RemoveLight()
|
||||
if not self.light then return end
|
||||
local light = self.light
|
||||
self.light = nil
|
||||
-- this prevents fade out when removing the light
|
||||
light.pos = Vector(9999, 9999, 9999)
|
||||
timer.Simple(0, function()
|
||||
light.dietime = 0
|
||||
end)
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local color = self:GetColor()
|
||||
local hue = pac.ColorToNames({r = color[1] * 255, g = color[2] * 255, b = color[3] * 255})
|
||||
return hue .. " light"
|
||||
end
|
||||
|
||||
local vars = {
|
||||
"InnerAngle",
|
||||
"OuterAngle",
|
||||
"NoWorld",
|
||||
"NoModel",
|
||||
"Brightness",
|
||||
"Color",
|
||||
"Size",
|
||||
}
|
||||
|
||||
function PART:OnShow()
|
||||
for _, v in ipairs(vars) do
|
||||
self["Set" .. v](self, self["Get" .. v](self))
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
self:GetLight().pos = pos
|
||||
self:GetLight().dir = ang:Forward()
|
||||
end
|
||||
|
||||
function PART:SetSize(val)
|
||||
self.Size = val
|
||||
self:GetLight().size = val
|
||||
end
|
||||
|
||||
function PART:SetColor(val)
|
||||
self.Color = val
|
||||
self:GetLight().r = math.Clamp(val.r * 255, 0, 255)
|
||||
self:GetLight().g = math.Clamp(val.g * 255, 0, 255)
|
||||
self:GetLight().b = math.Clamp(val.b * 255, 0, 255)
|
||||
end
|
||||
|
||||
function PART:SetBrightness(val)
|
||||
self.Brightness = val
|
||||
self:GetLight().brightness = val
|
||||
end
|
||||
|
||||
function PART:SetNoModel(val)
|
||||
self.NoModel = val
|
||||
self:GetLight().nomodel = val
|
||||
end
|
||||
|
||||
function PART:SetNoWorld(val)
|
||||
self.NoWorld = val
|
||||
self:GetLight().noworld = val
|
||||
end
|
||||
|
||||
function PART:SetInnerAngle(val)
|
||||
self.InnerAngle = val
|
||||
self:GetLight().innerangle = val
|
||||
end
|
||||
|
||||
function PART:SetOuterAngle(val)
|
||||
self.OuterAngle = val
|
||||
self:GetLight().outerangle = val
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self:RemoveLight()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
72
lua/pac3/core/client/parts/link.lua
Normal file
72
lua/pac3/core/client/parts/link.lua
Normal file
@@ -0,0 +1,72 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "link"
|
||||
|
||||
PART.Group = 'advanced'
|
||||
PART.Icon = 'icon16/weather_clouds.png'
|
||||
|
||||
local function fill_enums(target)
|
||||
if not target:IsValid() then return end
|
||||
|
||||
local tbl = {}
|
||||
for _, prop in pairs(target:GetProperties()) do
|
||||
if prop.key == "UniqueID" then goto CONTINUE end
|
||||
|
||||
local T = type(prop.get())
|
||||
if T == "number" or T == "Vector" or T == "Angle" or T == "boolean" then
|
||||
tbl[prop.key] = prop.key
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSetPart("From")
|
||||
BUILDER:GetSetPart("To")
|
||||
BUILDER:GetSet("Type", "from -> to", {enums = {"from -> to", "to -< from", "from <-> to"}})
|
||||
BUILDER:GetSet("FromVariableName", "", {enums = function(self) return fill_enums(self:GetFrom()) end})
|
||||
BUILDER:GetSet("ToVariableName", "", {enums = function(self) return fill_enums(self:GetTo()) end})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local function hook_property(a, b, a_prop, b_prop, callback)
|
||||
if not a["Get" .. a_prop] or not a["Set" .. a_prop] then return end
|
||||
if not b["Get" .. b_prop] or not b["Set" .. b_prop] then return end
|
||||
end
|
||||
|
||||
function PART:SetFrom(from)
|
||||
self.From = from
|
||||
|
||||
if self.FromVariableName == "" then return end
|
||||
if not from:IsValid() then return end
|
||||
|
||||
if not from["Set" .. self.FromVariableName] then return end
|
||||
|
||||
local old_from_setter = from["Set" .. self.FromVariableName]
|
||||
local from_getter = from["Get" .. self.FromVariableName]
|
||||
from["Set" .. self.FromVariableName] = function(s, v)
|
||||
old_from_setter(s, v)
|
||||
|
||||
local to = self:GetTo()
|
||||
if self.ToVariableName == "" then return end
|
||||
if not to:IsValid() then return end
|
||||
|
||||
local to_setter = to["Set" .. self.FromVariableName]
|
||||
if not to_setter then return end
|
||||
|
||||
to_setter(to, from_getter(from))
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
609
lua/pac3/core/client/parts/material.lua
Normal file
609
lua/pac3/core/client/parts/material.lua
Normal file
@@ -0,0 +1,609 @@
|
||||
--[[
|
||||
| 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 shader_params = include("pac3/libraries/shader_params.lua")
|
||||
|
||||
local mat_hdr_level = GetConVar("mat_hdr_level")
|
||||
|
||||
local material_flags = {
|
||||
debug = bit.lshift(1, 0),
|
||||
no_debug_override = bit.lshift(1, 1),
|
||||
no_draw = bit.lshift(1, 2),
|
||||
use_in_fillrate_mode = bit.lshift(1, 3),
|
||||
vertexcolor = bit.lshift(1, 4),
|
||||
vertexalpha = bit.lshift(1, 5),
|
||||
selfillum = bit.lshift(1, 6),
|
||||
additive = bit.lshift(1, 7),
|
||||
alphatest = bit.lshift(1, 8),
|
||||
multipass = bit.lshift(1, 9),
|
||||
znearer = bit.lshift(1, 10),
|
||||
model = bit.lshift(1, 11),
|
||||
flat = bit.lshift(1, 12),
|
||||
nocull = bit.lshift(1, 13),
|
||||
nofog = bit.lshift(1, 14),
|
||||
ignorez = bit.lshift(1, 15),
|
||||
decal = bit.lshift(1, 16),
|
||||
envmapsphere = bit.lshift(1, 17),
|
||||
noalphamod = bit.lshift(1, 18),
|
||||
envmapcameraspace = bit.lshift(1, 19),
|
||||
basealphaenvmapmask = bit.lshift(1, 20),
|
||||
translucent = bit.lshift(1, 21),
|
||||
normalmapalphaenvmapmask = bit.lshift(1, 22),
|
||||
needs_software_skinning = bit.lshift(1, 23),
|
||||
opaquetexture = bit.lshift(1, 24),
|
||||
envmapmode = bit.lshift(1, 25),
|
||||
suppress_decals = bit.lshift(1, 26),
|
||||
halflambert = bit.lshift(1, 27),
|
||||
wireframe = bit.lshift(1, 28),
|
||||
allowalphatocoverage = bit.lshift(1, 29),
|
||||
ignore_alpha_modulation = bit.lshift(1, 30),
|
||||
}
|
||||
|
||||
local function TableToFlags(flags, valid_flags)
|
||||
if isstring(flags) then
|
||||
flags = {flags}
|
||||
end
|
||||
|
||||
local out = 0
|
||||
|
||||
for k, v in pairs(flags) do
|
||||
if v then
|
||||
local flag = valid_flags[v] or valid_flags[k]
|
||||
if not flag then
|
||||
error("invalid flag", 2)
|
||||
end
|
||||
|
||||
out = bit.bor(out, tonumber(flag))
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
local function FlagsToTable(flags, valid_flags)
|
||||
|
||||
if not flags then return valid_flags.default_valid_flag end
|
||||
|
||||
local out = {}
|
||||
|
||||
for k, v in pairs(valid_flags) do
|
||||
if bit.band(flags, v) > 0 then
|
||||
out[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
local shader_name_translate = {
|
||||
vertexlitgeneric = "3d",
|
||||
unlitgeneric = "2d",
|
||||
eyerefract = "eye refract",
|
||||
}
|
||||
|
||||
for shader_name, groups in pairs(shader_params.shaders) do
|
||||
for group_name, base_group in pairs(shader_params.base) do
|
||||
if groups[group_name] then
|
||||
for k,v in pairs(base_group) do
|
||||
if not groups[group_name][k] then
|
||||
groups[group_name][k] = v
|
||||
end
|
||||
end
|
||||
else
|
||||
groups[group_name] = base_group
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for shader_name, groups in pairs(shader_params.shaders) do
|
||||
local temp = CreateMaterial(tostring({}), shader_name, {})
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "material_" .. (shader_name_translate[shader_name] or shader_name)
|
||||
PART.Description = shader_name
|
||||
|
||||
PART.ProperColorRange = true
|
||||
|
||||
if shader_name == "vertexlitgeneric" then
|
||||
PART.FriendlyName = "material"
|
||||
PART.Group = {'modifiers', 'model', 'entity'}
|
||||
else
|
||||
PART.FriendlyName = "material " .. shader_name
|
||||
PART.Group = "advanced"
|
||||
end
|
||||
|
||||
PART.Icon = "icon16/paintcan.png"
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
|
||||
-- move this to tools or something
|
||||
BUILDER:GetSet("LoadVmt", "", {editor_panel = "material"})
|
||||
function PART:SetLoadVmt(path)
|
||||
if not path or path == "" then return end
|
||||
|
||||
local str = file.Read("materials/" .. path .. ".vmt", "GAME")
|
||||
|
||||
if not str then return end
|
||||
|
||||
local vmt = util.KeyValuesToTable(str)
|
||||
local shader = str:match("^(.-)%{"):gsub("%p", ""):Trim()
|
||||
|
||||
|
||||
for k,v in pairs(self:GetVars()) do
|
||||
local param = PART.ShaderParams[k]
|
||||
if param and param.default ~= nil then
|
||||
self["Set" .. k](self, param.default)
|
||||
end
|
||||
if param and param.type == "texture" then
|
||||
self["Set" .. k](self, "")
|
||||
end
|
||||
end
|
||||
|
||||
print(str)
|
||||
print("======")
|
||||
PrintTable(vmt)
|
||||
print("======")
|
||||
|
||||
for k,v in pairs(vmt) do
|
||||
if k:StartWith("$") then k = k:sub(2) end
|
||||
|
||||
local func = self["Set" .. k]
|
||||
if func then
|
||||
local info = PART.ShaderParams[k]
|
||||
|
||||
if isstring(v) then
|
||||
if v:find("[", nil, true) then
|
||||
v = Vector(v:gsub("[%[%]]", ""):gsub("%s+", " "):Trim())
|
||||
|
||||
if isnumber(info.default) then
|
||||
v = v.x
|
||||
end
|
||||
elseif v:find("{", nil, true) then
|
||||
v = Vector(v:gsub("[%{%}]", ""):gsub("%s+", " "):Trim())
|
||||
|
||||
if info.type == "color" then
|
||||
v = v / 255
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isnumber(v) then
|
||||
if info.type == "bool" or info.is_flag then
|
||||
v = v == 1
|
||||
end
|
||||
end
|
||||
|
||||
func(self, v)
|
||||
else
|
||||
pac.Message("cannot convert material parameter " .. k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:GetSet("MaterialOverride", "all", {enums = function(self, str)
|
||||
|
||||
local materials = {}
|
||||
|
||||
if pace.current_part:GetOwner():IsValid() then
|
||||
materials = pace.current_part:GetOwner():GetMaterials()
|
||||
end
|
||||
|
||||
table.insert(materials, "all")
|
||||
|
||||
local tbl = {}
|
||||
|
||||
for _, v in ipairs(materials) do
|
||||
v = v:match(".+/(.+)") or v
|
||||
tbl[v] = v:lower()
|
||||
end
|
||||
|
||||
return tbl
|
||||
end})
|
||||
|
||||
local function update_submaterial(self, remove, parent)
|
||||
pac.RunNextFrameSimple(function()
|
||||
if not IsValid(self) and not remove then return end
|
||||
local name = self:GetName()
|
||||
|
||||
for _, part in ipairs(self:GetRootPart():GetChildrenList()) do
|
||||
if part.GetMaterials then
|
||||
for _, path in ipairs(part.Materials:Split(";")) do
|
||||
if path == name then
|
||||
part:SetMaterials(part.Materials)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local str = self.MaterialOverride
|
||||
parent = parent or self:GetParent()
|
||||
|
||||
local num = 0
|
||||
|
||||
if parent:IsValid() then
|
||||
if tonumber(str) then
|
||||
num = tonumber(str)
|
||||
elseif str ~= "all" and parent:GetOwner():IsValid() then
|
||||
for i, v in ipairs(parent:GetOwner():GetMaterials()) do
|
||||
if (v:match(".+/(.+)") or v):lower() == str:lower() then
|
||||
num = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parent.material_override = parent.material_override or {}
|
||||
parent.material_override[num] = parent.material_override[num] or {}
|
||||
|
||||
for _, stack in pairs(parent.material_override) do
|
||||
for i, v in ipairs(stack) do
|
||||
if v == self then
|
||||
table.remove(stack, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not remove then
|
||||
table.insert(parent.material_override[num], self)
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self.translation_vector = Vector()
|
||||
self.rotation_angle = Angle(0, 0, 0)
|
||||
end
|
||||
|
||||
|
||||
function PART:GetNiceName()
|
||||
local path = ""
|
||||
|
||||
if shader_name == "refract" then
|
||||
path = self:Getnormalmap()
|
||||
elseif shader_name == "eyerefract" then
|
||||
path = self:Getiris()
|
||||
else
|
||||
path = self:Getbasetexture()
|
||||
end
|
||||
|
||||
path = path:gsub("%%(..)", function(char)
|
||||
local num = tonumber("0x" .. char)
|
||||
if num then
|
||||
return string.char(num)
|
||||
end
|
||||
end)
|
||||
|
||||
local name = ("/".. path):match(".+/(.-)%.") or ("/".. path):match(".+/(.+)")
|
||||
local nice_name = (pac.PrettifyName(name) or "no texture") .. " | " .. shader_name
|
||||
|
||||
return nice_name
|
||||
end
|
||||
|
||||
function PART:SetMaterialOverride(num)
|
||||
self.MaterialOverride = num
|
||||
|
||||
update_submaterial(self)
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
if self:GetOwner():IsValid() then
|
||||
local materials = self:GetOwner():GetMaterials()
|
||||
if materials and #materials ~= self.last_material_count then
|
||||
update_submaterial(self)
|
||||
self.last_material_count = #materials
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PART.ShaderParams = {}
|
||||
PART.TransformVars = {}
|
||||
|
||||
local sorted_groups = {}
|
||||
for k, v in pairs(groups) do
|
||||
table.insert(sorted_groups, {k = k, v = v})
|
||||
end
|
||||
table.sort(sorted_groups, function(a, b) return a.k:lower() < b.k:lower() end)
|
||||
|
||||
for _, v in ipairs(sorted_groups) do
|
||||
local group, params = v.k, v.v
|
||||
|
||||
local sorted_params = {}
|
||||
for k, v in pairs(params) do
|
||||
table.insert(sorted_params, {k = k, v = v})
|
||||
end
|
||||
table.sort(sorted_params, function(a, b) return a.k:lower() < b.k:lower() end)
|
||||
|
||||
for _, v in ipairs(sorted_params) do
|
||||
local key, info = v.k, v.v
|
||||
|
||||
PART.ShaderParams[key] = info
|
||||
|
||||
if info.is_flag and group == "generic" then
|
||||
BUILDER:SetPropertyGroup("flags")
|
||||
else
|
||||
BUILDER:SetPropertyGroup(group)
|
||||
end
|
||||
|
||||
if info.default == nil then
|
||||
if info.type == "vec3" then
|
||||
info.default = Vector(0,0,0)
|
||||
elseif info.type == "color" then
|
||||
info.default = Vector(1,1,1)
|
||||
elseif info.type == "float" then
|
||||
info.default = 0
|
||||
elseif info.type == "vec2" then
|
||||
info.default = Vector(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local property_name = key
|
||||
|
||||
local description = (info.description or "") .. " ($" .. key .. ")"
|
||||
|
||||
if info.type == "matrix" then
|
||||
local position_key = property_name .. "Position"
|
||||
local scale_key = property_name .. "Scale"
|
||||
local angle_key = property_name .. "Angle"
|
||||
local angle_center_key = property_name .. "AngleCenter"
|
||||
|
||||
local friendly_name = info.friendly:gsub("Transform", "")
|
||||
BUILDER:GetSet(position_key, Vector(0, 0, 0), {editor_friendly = friendly_name .. "Position", description = description})
|
||||
BUILDER:GetSet(scale_key, Vector(1, 1, 1), {editor_friendly = friendly_name .. "Scale", description = description})
|
||||
BUILDER:GetSet(angle_key, 0, {editor_panel = "number", editor_friendly = friendly_name .. "Angle", description = description})
|
||||
BUILDER:GetSet(angle_center_key, Vector(0.5, 0.5, 0), {editor_friendly = friendly_name .. "AngleCenter", description = description})
|
||||
|
||||
PART.TransformVars[position_key] = true
|
||||
PART.TransformVars[scale_key] = true
|
||||
PART.TransformVars[angle_key] = true
|
||||
PART.TransformVars[angle_center_key] = true
|
||||
|
||||
local shader_key = "$" .. key
|
||||
|
||||
local function setup_matrix(self)
|
||||
self.matrix = self.matrix or Matrix()
|
||||
|
||||
self.matrix:Identity()
|
||||
self.matrix:Translate(self.translation_vector)
|
||||
|
||||
self.matrix:Translate(self[angle_center_key])
|
||||
self.matrix:Rotate(self.rotation_angle)
|
||||
self.matrix:Translate(-self[angle_center_key])
|
||||
|
||||
self.matrix:SetScale(self[scale_key])
|
||||
end
|
||||
|
||||
PART["Set" .. position_key] = function(self, vec)
|
||||
self[position_key] = vec
|
||||
|
||||
|
||||
self.translation_vector.x = self[position_key].x
|
||||
self.translation_vector.y = self[position_key].y
|
||||
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. scale_key] = function(self, vec)
|
||||
self[scale_key] = vec
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. angle_key] = function(self, num)
|
||||
self[angle_key] = num
|
||||
|
||||
self.rotation_angle.y = self[angle_key]*360
|
||||
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
|
||||
PART["Set" .. angle_center_key] = function(self, vec)
|
||||
self[angle_center_key] = vec
|
||||
|
||||
setup_matrix(self)
|
||||
|
||||
self:GetRawMaterial():SetMatrix(shader_key, self.matrix)
|
||||
end
|
||||
elseif info.type == "texture" then
|
||||
local getnohdr = "Get" .. property_name .. "NoHDR"
|
||||
|
||||
if info.partial_hdr then
|
||||
BUILDER:GetSet(property_name .. "NoHDR", false, {
|
||||
editor_friendly = info.friendly .. " No HDR",
|
||||
description = "Disables bound param when HDR is enabled",
|
||||
})
|
||||
end
|
||||
|
||||
info.default = info.default or ""
|
||||
|
||||
BUILDER:GetSet(property_name, info.default, {
|
||||
editor_panel = "textures",
|
||||
editor_friendly = info.friendly,
|
||||
description = description,
|
||||
shader_param_info = info,
|
||||
})
|
||||
|
||||
local key = "$" .. key
|
||||
|
||||
PART["Set" .. property_name .. "NoHDR"] = function(self, val)
|
||||
self[property_name .. "NoHDR"] = val
|
||||
PART["Set" .. property_name](self, self[property_name])
|
||||
end
|
||||
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
self[property_name] = val
|
||||
|
||||
if val == "" or info.partial_hdr and mat_hdr_level:GetInt() > 0 and self[getnohdr](self) then
|
||||
self:GetRawMaterial():SetUndefined(key)
|
||||
self:GetRawMaterial():Recompute()
|
||||
else
|
||||
if not pac.resource.DownloadTexture(val, function(tex, frames)
|
||||
if frames then
|
||||
self.vtf_frame_limit = self.vtf_frame_limit or {}
|
||||
self.vtf_frame_limit[property_name] = frames
|
||||
end
|
||||
self:GetRawMaterial():SetTexture(key, tex)
|
||||
end, self:GetPlayerOwner()) then
|
||||
self:GetRawMaterial():SetTexture(key, val)
|
||||
|
||||
local texture = self:GetRawMaterial():GetTexture(key)
|
||||
|
||||
if texture then
|
||||
self.vtf_frame_limit = self.vtf_frame_limit or {}
|
||||
self.vtf_frame_limit[property_name] = texture:GetNumAnimationFrames()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
BUILDER:GetSet(property_name, info.default, {
|
||||
editor_friendly = info.friendly,
|
||||
enums = info.enums,
|
||||
description = description,
|
||||
editor_sensitivity = (info.type == "vec3" or info.type == "color") and 0.25 or nil,
|
||||
editor_panel = (info.type == "color" and "color2") or (property_name == "model" and "boolean") or nil,
|
||||
editor_round = info.type == "integer",
|
||||
})
|
||||
|
||||
local flag_key = key
|
||||
local key = "$" .. key
|
||||
|
||||
if isnumber(info.default) then
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
self[property_name] = val
|
||||
local mat = self:GetRawMaterial()
|
||||
mat:SetFloat(key, val)
|
||||
if info.recompute then
|
||||
mat:Recompute()
|
||||
end
|
||||
end
|
||||
if property_name:lower():find("frame") then
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
self[property_name] = val
|
||||
if self.vtf_frame_limit and info.linked and self.vtf_frame_limit[info.linked] then
|
||||
self:GetRawMaterial():SetInt(key, math.abs(val)%self.vtf_frame_limit[info.linked])
|
||||
else
|
||||
self:GetRawMaterial():SetInt(key, val)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif isbool(info.default) then
|
||||
if info.is_flag then
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
self[property_name] = val
|
||||
|
||||
local mat = self:GetRawMaterial()
|
||||
|
||||
local tbl = FlagsToTable(mat:GetInt("$flags"), material_flags)
|
||||
tbl[flag_key] = val
|
||||
mat:SetInt("$flags", TableToFlags(tbl, material_flags))
|
||||
|
||||
mat:Recompute()
|
||||
end
|
||||
else
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
if isvector(val) then
|
||||
val = (val == Vector(1,1,1)) and true or false
|
||||
end
|
||||
|
||||
self[property_name] = val
|
||||
local mat = self:GetRawMaterial()
|
||||
|
||||
mat:SetInt(key, val and 1 or 0)
|
||||
if info.recompute then mat:Recompute() end
|
||||
end
|
||||
end
|
||||
elseif isvector(info.default) or info.type == "vec3" or info.type == "vec2" then
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
if isstring(val) then val = Vector() end
|
||||
self[property_name] = val
|
||||
local mat = self:GetRawMaterial()
|
||||
mat:SetVector(key, val)
|
||||
if info.recompute then mat:Recompute() end
|
||||
end
|
||||
elseif info.type == "vec4" then
|
||||
-- need vec4 type
|
||||
PART["Set" .. property_name] = function(self, val)
|
||||
|
||||
local x,y,z,w
|
||||
if isstring(val) then
|
||||
x,y,z,w = unpack(val:Split(" "))
|
||||
x = tonumber(x) or 0
|
||||
y = tonumber(y) or 0
|
||||
z = tonumber(z) or 0
|
||||
w = tonumber(w) or 0
|
||||
elseif isvector(val) then
|
||||
x,y,z = val.x, val.y, val.z
|
||||
w = 0
|
||||
else
|
||||
x, y, z, w = 0, 0, 0, 0
|
||||
end
|
||||
|
||||
self[property_name] = ("%f %f %f %f"):format(x, y, z, w)
|
||||
local mat = self:GetRawMaterial()
|
||||
mat:SetString(key, ("[%f %f %f %f]"):format(x,y,z,w))
|
||||
|
||||
if info.recompute then mat:Recompute() end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetRawMaterial()
|
||||
if not self.Materialm then
|
||||
self.material_name = tostring({})
|
||||
local mat = pac.CreateMaterial(self.material_name, shader_name, {})
|
||||
self.Materialm = mat
|
||||
|
||||
for k,v in pairs(self:GetVars()) do
|
||||
if PART.ShaderParams[k] and PART.ShaderParams[k].default ~= nil then
|
||||
self["Set" .. k](self, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self.Materialm
|
||||
end
|
||||
|
||||
function PART:OnParent(parent)
|
||||
update_submaterial(self)
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
update_submaterial(self, true)
|
||||
end
|
||||
|
||||
function PART:OnUnParent(parent)
|
||||
update_submaterial(self, true, parent)
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
update_submaterial(self, true)
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
update_submaterial(self)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
end
|
||||
909
lua/pac3/core/client/parts/model.lua
Normal file
909
lua/pac3/core/client/parts/model.lua
Normal file
@@ -0,0 +1,909 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
CreateConVar( "pac_model_max_scales", "10000", FCVAR_ARCHIVE, "Maximum scales model can have")
|
||||
|
||||
|
||||
|
||||
|
||||
local pac = pac
|
||||
|
||||
local render_SetColorModulation = render.SetColorModulation
|
||||
local render_SetBlend = render.SetBlend
|
||||
local render_CullMode = render.CullMode
|
||||
local MATERIAL_CULLMODE_CW = MATERIAL_CULLMODE_CW
|
||||
local MATERIAL_CULLMODE_CCW = MATERIAL_CULLMODE_CCW
|
||||
local render_MaterialOverride = render.ModelMaterialOverride
|
||||
local cam_PushModelMatrix = cam.PushModelMatrix
|
||||
local cam_PopModelMatrix = cam.PopModelMatrix
|
||||
local Vector = Vector
|
||||
local EF_BONEMERGE = EF_BONEMERGE
|
||||
local NULL = NULL
|
||||
local Color = Color
|
||||
local Matrix = Matrix
|
||||
local vector_origin = vector_origin
|
||||
local render = render
|
||||
local cam = cam
|
||||
local surface = surface
|
||||
local render_MaterialOverrideByIndex = render.MaterialOverrideByIndex
|
||||
local render_SuppressEngineLighting = render.SuppressEngineLighting
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "model"
|
||||
PART.ClassName = "model2"
|
||||
PART.Category = "model"
|
||||
PART.ManualDraw = true
|
||||
PART.HandleModifiersManually = true
|
||||
PART.Icon = 'icon16/shape_square.png'
|
||||
PART.is_model_part = true
|
||||
PART.ProperColorRange = true
|
||||
PART.Group = 'model'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:SetPropertyGroup("generic")
|
||||
:PropertyOrder("Name")
|
||||
:PropertyOrder("Hide")
|
||||
:PropertyOrder("ParentName")
|
||||
:GetSet("Model", "", {editor_panel = "model"})
|
||||
:GetSet("ForceObjUrl", false)
|
||||
|
||||
:SetPropertyGroup("orientation")
|
||||
:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
:GetSet("Scale", Vector(1,1,1))
|
||||
:GetSet("BoneMerge", false)
|
||||
:GetSet("LegacyTransform", false)
|
||||
|
||||
:SetPropertyGroup("appearance")
|
||||
:GetSet("Color", Vector(1, 1, 1), {editor_panel = "color2"})
|
||||
:GetSet("Brightness", 1)
|
||||
:GetSet("NoLighting", false)
|
||||
:GetSet("NoCulling", false)
|
||||
:GetSet("Invert", false)
|
||||
:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
:GetSet("ModelModifiers", "", {hidden = true})
|
||||
:GetSet("Material", "", {editor_panel = "material"})
|
||||
:GetSet("Materials", "", {hidden = true})
|
||||
:GetSet("Skin", 0, {editor_onchange = function(self, num) return math.Round(math.Clamp(tonumber(num), 0, pace.current_part:GetOwner():SkinCount())) end})
|
||||
:GetSet("LevelOfDetail", 0, {editor_clamp = {-1, 8}, editor_round = true})
|
||||
:GetSetPart("EyeTarget")
|
||||
|
||||
:EndStorableVars()
|
||||
|
||||
PART.Owner = NULL
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName(("/" .. self:GetModel()):match(".+/(.-)%."))
|
||||
|
||||
return str and str:gsub("%d", "") or "error"
|
||||
end
|
||||
|
||||
function PART:GetDynamicProperties()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() or not ent:GetBodyGroups() then return end
|
||||
|
||||
local tbl = {}
|
||||
|
||||
if ent:SkinCount() and ent:SkinCount() > 1 then
|
||||
tbl.skin = {
|
||||
key = "skin",
|
||||
set = function(val)
|
||||
local tbl = self:ModelModifiersToTable(self:GetModelModifiers())
|
||||
tbl.skin = val
|
||||
self:SetModelModifiers(self:ModelModifiersToString(tbl))
|
||||
end,
|
||||
get = function()
|
||||
return self:ModelModifiersToTable(self:GetModelModifiers()).skin
|
||||
end,
|
||||
udata = {editor_onchange = function(self, num) return math.Clamp(math.Round(num), 0, ent:SkinCount() - 1) end},
|
||||
}
|
||||
end
|
||||
|
||||
for _, info in ipairs(ent:GetBodyGroups()) do
|
||||
if info.num > 1 then
|
||||
tbl[info.name] = {
|
||||
key = info.name,
|
||||
set = function(val)
|
||||
local tbl = self:ModelModifiersToTable(self:GetModelModifiers())
|
||||
tbl[info.name] = val
|
||||
self:SetModelModifiers(self:ModelModifiersToString(tbl))
|
||||
end,
|
||||
get = function()
|
||||
return self:ModelModifiersToTable(self:GetModelModifiers())[info.name] or 0
|
||||
end,
|
||||
udata = {editor_onchange = function(self, num) return math.Clamp(math.Round(num), 0, info.num - 1) end, group = "bodygroups"},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if ent:GetMaterials() and #ent:GetMaterials() > 1 then
|
||||
for i, name in ipairs(ent:GetMaterials()) do
|
||||
name = name:match(".+/(.+)") or name
|
||||
tbl[name] = {
|
||||
key = name,
|
||||
get = function()
|
||||
local tbl = self.Materials:Split(";")
|
||||
return tbl[i] or ""
|
||||
end,
|
||||
set = function(val)
|
||||
local tbl = self.Materials:Split(";")
|
||||
tbl[i] = val
|
||||
|
||||
for i, name in ipairs(ent:GetMaterials()) do
|
||||
tbl[i] = tbl[i] or ""
|
||||
end
|
||||
|
||||
self:SetMaterials(table.concat(tbl, ";"))
|
||||
end,
|
||||
udata = {editor_panel = "material", editor_friendly = name, group = "sub materials"},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
function PART:SetLevelOfDetail(val)
|
||||
self.LevelOfDetail = val
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() then
|
||||
ent:SetLOD(val)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSkin(var)
|
||||
self.Skin = var
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
owner:SetSkin(var)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:ModelModifiersToTable(str)
|
||||
if str == "" or (not str:find(";", nil, true) and not str:find("=", nil, true)) then return {} end
|
||||
|
||||
local tbl = {}
|
||||
for _, data in ipairs(str:Split(";")) do
|
||||
local key, val = data:match("(.+)=(.+)")
|
||||
if key then
|
||||
key = key:Trim()
|
||||
val = tonumber(val:Trim())
|
||||
|
||||
tbl[key] = val
|
||||
end
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
function PART:ModelModifiersToString(tbl)
|
||||
local str = ""
|
||||
for k,v in pairs(tbl) do
|
||||
str = str .. k .. "=" .. v .. ";"
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
function PART:SetModelModifiers(str)
|
||||
self.ModelModifiers = str
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
local tbl = self:ModelModifiersToTable(str)
|
||||
|
||||
if tbl.skin then
|
||||
self:SetSkin(tbl.skin)
|
||||
tbl.skin = nil
|
||||
end
|
||||
|
||||
if not owner:GetBodyGroups() then return end
|
||||
|
||||
self.draw_bodygroups = {}
|
||||
|
||||
for i, info in ipairs(owner:GetBodyGroups()) do
|
||||
local val = tbl[info.name]
|
||||
if val then
|
||||
table.insert(self.draw_bodygroups, {info.id, val})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMaterial(str)
|
||||
self.Material = str
|
||||
|
||||
if str == "" then
|
||||
if self.material_override_self then
|
||||
self.material_override_self[0] = nil
|
||||
end
|
||||
else
|
||||
self.material_override_self = self.material_override_self or {}
|
||||
|
||||
if not pac.Handleurltex(self, str, function(mat)
|
||||
self.material_override_self = self.material_override_self or {}
|
||||
self.material_override_self[0] = mat
|
||||
end) then
|
||||
self.material_override_self[0] = pac.Material(str, self)
|
||||
end
|
||||
end
|
||||
|
||||
if self.material_override_self and not next(self.material_override_self) then
|
||||
self.material_override_self = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMaterials(str)
|
||||
self.Materials = str
|
||||
|
||||
local materials = self:GetOwner():IsValid() and self:GetOwner():GetMaterials()
|
||||
|
||||
if not materials then return end
|
||||
|
||||
self.material_count = #materials
|
||||
|
||||
self.material_override_self = self.material_override_self or {}
|
||||
|
||||
local tbl = str:Split(";")
|
||||
|
||||
for i = 1, #materials do
|
||||
local path = tbl[i]
|
||||
|
||||
if path and path ~= "" then
|
||||
if not pac.Handleurltex(self, path, function(mat)
|
||||
self.material_override_self = self.material_override_self or {}
|
||||
self.material_override_self[i] = mat
|
||||
end) then
|
||||
self.material_override_self[i] = pac.Material(path, self)
|
||||
end
|
||||
else
|
||||
self.material_override_self[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not next(self.material_override_self) then
|
||||
self.material_override_self = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:Reset()
|
||||
self:Initialize()
|
||||
for _, key in pairs(self:GetStorableVars()) do
|
||||
if PART[key] then
|
||||
self["Set" .. key](self, self["Get" .. key](self))
|
||||
end
|
||||
end
|
||||
self.cached_dynamic_props = nil
|
||||
end
|
||||
|
||||
function PART:OnBecomePhysics()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
ent:PhysicsInit(SOLID_NONE)
|
||||
ent:SetMoveType(MOVETYPE_NONE)
|
||||
ent:SetNoDraw(true)
|
||||
ent.RenderOverride = nil
|
||||
|
||||
self.skip_orient = false
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self.Owner = pac.CreateEntity(self:GetModel())
|
||||
self.Owner:SetNoDraw(true)
|
||||
self.Owner.PACPart = self
|
||||
self.material_count = 0
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local owner = self:GetParentOwner()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and owner:IsValid() and owner ~= ent then
|
||||
ent:SetPos(owner:EyePos())
|
||||
ent:SetLegacyTransform(self.LegacyTransform)
|
||||
self:SetBone(self:GetBone())
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
if not self.loading then
|
||||
SafeRemoveEntityDelayed(self.Owner,0.1)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
self:CheckBoneMerge()
|
||||
end
|
||||
|
||||
function PART:BindMaterials(ent)
|
||||
local materials = self.material_override_self or self.material_override
|
||||
local material_bound = false
|
||||
|
||||
if self.material_override_self then
|
||||
if materials[0] then
|
||||
render_MaterialOverride(materials[0])
|
||||
material_bound = true
|
||||
end
|
||||
|
||||
for i = 1, self.material_count do
|
||||
local mat = materials[i]
|
||||
|
||||
if mat then
|
||||
render_MaterialOverrideByIndex(i-1, mat)
|
||||
else
|
||||
render_MaterialOverrideByIndex(i-1, nil)
|
||||
end
|
||||
end
|
||||
elseif self.material_override then
|
||||
if materials[0] and materials[0][1] then
|
||||
render_MaterialOverride(materials[0][1]:GetRawMaterial())
|
||||
material_bound = true
|
||||
end
|
||||
|
||||
for i = 1, self.material_count do
|
||||
local stack = materials[i]
|
||||
if stack then
|
||||
local mat = stack[1]
|
||||
|
||||
if mat then
|
||||
render_MaterialOverrideByIndex(i-1, mat:GetRawMaterial())
|
||||
else
|
||||
render_MaterialOverrideByIndex(i-1, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.BoneMerge and not material_bound then
|
||||
render_MaterialOverride()
|
||||
end
|
||||
|
||||
return material_bound
|
||||
end
|
||||
|
||||
function PART:PreEntityDraw(ent, pos, ang)
|
||||
if not ent:IsPlayer() and pos and ang then
|
||||
if not self.skip_orient then
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
end
|
||||
end
|
||||
|
||||
if self.Alpha ~= 0 and self.Size ~= 0 then
|
||||
self:ModifiersPreEvent("OnDraw")
|
||||
|
||||
local r, g, b = self.Color.r, self.Color.g, self.Color.b
|
||||
local brightness = self.Brightness
|
||||
|
||||
-- render.SetColorModulation and render.SetAlpha set the material $color and $alpha.
|
||||
render_SetColorModulation(r*brightness, g*brightness, b*brightness)
|
||||
if not pac.drawing_motionblur_alpha then
|
||||
render_SetBlend(self.Alpha)
|
||||
end
|
||||
|
||||
if self.NoLighting then
|
||||
render_SuppressEngineLighting(true)
|
||||
end
|
||||
end
|
||||
|
||||
if self.draw_bodygroups then
|
||||
for _, v in ipairs(self.draw_bodygroups) do
|
||||
ent:SetBodygroup(v[1], v[2])
|
||||
end
|
||||
end
|
||||
|
||||
if self.EyeTarget:IsValid() and self.EyeTarget.GetWorldPosition then
|
||||
ent:SetEyeTarget(self.EyeTarget:GetWorldPosition())
|
||||
ent.pac_modified_eyetarget = true
|
||||
elseif ent.pac_modified_eyetarget then
|
||||
ent:SetEyeTarget(vector_origin)
|
||||
ent.pac_modified_eyetarget = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PostEntityDraw(ent, pos, ang)
|
||||
if self.Alpha ~= 0 and self.Size ~= 0 then
|
||||
self:ModifiersPostEvent("OnDraw")
|
||||
|
||||
if self.NoLighting then
|
||||
render_SuppressEngineLighting(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then
|
||||
self:Reset()
|
||||
ent = self:GetOwner()
|
||||
end
|
||||
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
if self.loading then
|
||||
self:DrawLoadingText(ent, pos)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
self:PreEntityDraw(ent, pos, ang)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
self:PostEntityDraw(ent, pos, ang)
|
||||
|
||||
pac.ResetBones(ent)
|
||||
end
|
||||
|
||||
|
||||
local matrix = Matrix()
|
||||
local IDENT_SCALE = Vector(1,1,1)
|
||||
local _self, _ent, _pos, _ang
|
||||
|
||||
local function ent_draw_model(self, ent, pos, ang)
|
||||
if self.obj_mesh then
|
||||
ent:SetModelScale(0,0)
|
||||
ent:DrawModel()
|
||||
|
||||
matrix:Identity()
|
||||
matrix:SetAngles(ang)
|
||||
matrix:SetTranslation(pos)
|
||||
matrix:SetScale(self.Scale * self.Size)
|
||||
|
||||
cam_PushModelMatrix(matrix)
|
||||
self.obj_mesh:Draw()
|
||||
cam_PopModelMatrix()
|
||||
else
|
||||
if ent.needs_setupbones_from_legacy_bone_parts then
|
||||
pac.SetupBones(ent)
|
||||
ent.needs_setupbones_from_legacy_bone_parts = nil
|
||||
end
|
||||
ent:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
local function protected_ent_draw_model()
|
||||
ent_draw_model(_self, _ent, _pos, _ang)
|
||||
end
|
||||
|
||||
function PART:DrawModel(ent, pos, ang)
|
||||
if self.loading then
|
||||
self:DrawLoadingText(ent, pos)
|
||||
end
|
||||
|
||||
if self.Alpha == 0 or self.Size == 0 then return end
|
||||
if self.loading and not self.obj_mesh then return end
|
||||
|
||||
if self.NoCulling or self.Invert then
|
||||
render_CullMode(MATERIAL_CULLMODE_CW)
|
||||
end
|
||||
|
||||
local material_bound = false
|
||||
|
||||
material_bound = self:BindMaterials(ent) or material_bound
|
||||
|
||||
ent.pac_drawing_model = true
|
||||
ent_draw_model(self, ent, pos, ang)
|
||||
ent.pac_drawing_model = false
|
||||
|
||||
_self, _ent, _pos, _ang = self, ent, pos, ang
|
||||
|
||||
if self.ClassName ~= "entity2" then
|
||||
render.PushFlashlightMode(true)
|
||||
|
||||
material_bound = self:BindMaterials(ent) or material_bound
|
||||
ent.pac_drawing_model = true
|
||||
ProtectedCall(protected_ent_draw_model)
|
||||
ent.pac_drawing_model = false
|
||||
|
||||
render.PopFlashlightMode()
|
||||
end
|
||||
|
||||
if self.NoCulling then
|
||||
render_CullMode(MATERIAL_CULLMODE_CCW)
|
||||
material_bound = self:BindMaterials(ent) or material_bound
|
||||
ProtectedCall(protected_ent_draw_model)
|
||||
elseif self.Invert then
|
||||
render_CullMode(MATERIAL_CULLMODE_CCW)
|
||||
end
|
||||
|
||||
-- need to unbind mateiral
|
||||
if material_bound then
|
||||
render_MaterialOverride()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:DrawLoadingText(ent, pos)
|
||||
cam.Start2D()
|
||||
cam.IgnoreZ(true)
|
||||
local pos2d = pos:ToScreen()
|
||||
|
||||
surface.SetFont("DermaDefault")
|
||||
|
||||
if self.errored then
|
||||
surface.SetTextColor(255, 0, 0, 255)
|
||||
local str = self.loading:match("^(.-):\n") or self.loading:match("^(.-)\n") or self.loading:sub(1, 100)
|
||||
local w, h = surface.GetTextSize(str)
|
||||
surface.SetTextPos(pos2d.x - w / 2, pos2d.y - h / 2)
|
||||
surface.DrawText(str)
|
||||
self:SetError(str)
|
||||
else
|
||||
surface.SetTextColor(255, 255, 255, 255)
|
||||
local str = self.loading .. string.rep(".", pac.RealTime * 3 % 3)
|
||||
local w, h = surface.GetTextSize(self.loading .. "...")
|
||||
|
||||
surface.SetTextPos(pos2d.x - w / 2, pos2d.y - h / 2)
|
||||
surface.DrawText(str)
|
||||
self:SetError()
|
||||
end
|
||||
cam.IgnoreZ(false)
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
local ALLOW_TO_MDL = CreateConVar('pac_allow_mdl', '1', CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Allow to use custom MDLs')
|
||||
|
||||
function PART:RefreshModel()
|
||||
if self.refreshing_model then return end
|
||||
|
||||
self.refreshing_model = true
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
pac.ResetBoneCache(ent)
|
||||
end
|
||||
|
||||
self.cached_dynamic_props = nil
|
||||
|
||||
self:SetModelModifiers(self:GetModelModifiers())
|
||||
self:SetMaterials(self:GetMaterials())
|
||||
self:SetSize(self:GetSize())
|
||||
self:SetScale(self:GetScale())
|
||||
self:SetSkin(self:GetSkin())
|
||||
self:SetLevelOfDetail(self:GetLevelOfDetail())
|
||||
|
||||
if not self:IsHidden() and not self:IsDrawHidden() then
|
||||
-- notify children about model change
|
||||
self:ShowFromRendering()
|
||||
end
|
||||
|
||||
self.refreshing_model = false
|
||||
end
|
||||
|
||||
function PART:RealSetModel(path)
|
||||
self:GetOwner():SetModel(path)
|
||||
self:RefreshModel()
|
||||
end
|
||||
|
||||
function PART:SetForceObjUrl(value)
|
||||
self.ForceObjUrl = value
|
||||
self:ProcessModelChange()
|
||||
end
|
||||
|
||||
local function RealDrawModel(self, ent, pos, ang)
|
||||
if self.Mesh then
|
||||
ent:SetModelScale(0,0)
|
||||
ent:DrawModel()
|
||||
|
||||
local matrix = Matrix()
|
||||
|
||||
matrix:SetAngles(ang)
|
||||
matrix:SetTranslation(pos)
|
||||
|
||||
if ent.pac_model_scale then
|
||||
matrix:Scale(ent.pac_model_scale)
|
||||
else
|
||||
matrix:Scale(self.Scale * self.Size)
|
||||
end
|
||||
|
||||
cam_PushModelMatrix(matrix)
|
||||
self.Mesh:Draw()
|
||||
cam_PopModelMatrix()
|
||||
else
|
||||
ent:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:ProcessModelChange()
|
||||
local owner = self:GetOwner()
|
||||
if not owner:IsValid() then return end
|
||||
local path = self.Model
|
||||
|
||||
if path:find("://", nil, true) then
|
||||
if path:StartWith("objhttp") or path:StartWith("obj:http") or path:match("%.obj%p?") or self.ForceObjUrl then
|
||||
path = path:gsub("^objhttp","http"):gsub("^obj:http","http")
|
||||
self.loading = "downloading obj"
|
||||
|
||||
pac.urlobj.GetObjFromURL(path, false, false,
|
||||
function(meshes, err)
|
||||
|
||||
local function set_mesh(part, mesh)
|
||||
local owner = part:GetOwner()
|
||||
part.obj_mesh = mesh
|
||||
pac.ResetBoneCache(owner)
|
||||
|
||||
if not part.Materialm then
|
||||
part.Materialm = Material("error")
|
||||
end
|
||||
|
||||
function owner.pacDrawModel(ent, simple)
|
||||
if simple then
|
||||
RealDrawModel(part, ent, ent:GetPos(), ent:GetAngles())
|
||||
else
|
||||
part:ModifiersPreEvent("OnDraw")
|
||||
part:DrawModel(ent, ent:GetPos(), ent:GetAngles())
|
||||
part:ModifiersPostEvent("OnDraw")
|
||||
end
|
||||
end
|
||||
|
||||
owner:SetRenderBounds(Vector(1, 1, 1) * -300, Vector(1, 1, 1) * 300)
|
||||
end
|
||||
|
||||
if not self:IsValid() then return end
|
||||
|
||||
self.loading = false
|
||||
|
||||
if not meshes and err then
|
||||
owner:SetModel("models/error.mdl")
|
||||
self.obj_mesh = nil
|
||||
return
|
||||
end
|
||||
|
||||
if table.Count(meshes) == 1 then
|
||||
set_mesh(self, select(2, next(meshes)))
|
||||
else
|
||||
for key, mesh in pairs(meshes) do
|
||||
local part = pac.CreatePart("model", self:GetOwnerName())
|
||||
part:SetName(key)
|
||||
part:SetParent(self)
|
||||
part:SetMaterial(self:GetMaterial())
|
||||
set_mesh(part, mesh)
|
||||
end
|
||||
|
||||
self:SetAlpha(0)
|
||||
end
|
||||
end,
|
||||
|
||||
function(finished, statusMessage)
|
||||
if finished then
|
||||
self.loading = nil
|
||||
else
|
||||
self.loading = statusMessage
|
||||
end
|
||||
end
|
||||
)
|
||||
else
|
||||
local status, reason = hook.Run('PAC3AllowMDLDownload', self:GetPlayerOwner(), self, path)
|
||||
|
||||
if ALLOW_TO_MDL:GetBool() and status ~= false then
|
||||
self.loading = "downloading mdl zip"
|
||||
pac.DownloadMDL(path, function(mdl_path)
|
||||
self.loading = nil
|
||||
self.errored = nil
|
||||
|
||||
if self.ClassName == "entity2" then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "model", self:GetOwner(), path)
|
||||
end
|
||||
|
||||
self:RealSetModel(mdl_path)
|
||||
|
||||
end, function(err)
|
||||
|
||||
if pace and pace.current_part == self and not IsValid(pace.BusyWithProperties) then
|
||||
pace.MessagePrompt(err, "HTTP Request Failed for " .. path, "OK")
|
||||
else
|
||||
pac.Message(Color(0, 255, 0), "[model] ", Color(255, 255, 255), "HTTP Request Failed for " .. path .. " - " .. err)
|
||||
end
|
||||
|
||||
self.loading = err
|
||||
self.errored = true
|
||||
self:RealSetModel("models/error.mdl")
|
||||
end, self:GetPlayerOwner())
|
||||
else
|
||||
local msg = reason or "mdl's are not allowed"
|
||||
self.loading = msg
|
||||
self:SetError(msg)
|
||||
self:RealSetModel("models/error.mdl")
|
||||
pac.Message(self, msg)
|
||||
end
|
||||
end
|
||||
elseif path ~= "" then
|
||||
if self.ClassName == "entity2" then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "model", owner, path)
|
||||
end
|
||||
|
||||
self:RealSetModel(path)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetModel(path)
|
||||
self.Model = path
|
||||
|
||||
local owner = self:GetOwner()
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
self.old_model = path
|
||||
self:ProcessModelChange()
|
||||
end
|
||||
|
||||
local NORMAL = Vector(1,1,1)
|
||||
|
||||
function PART:CheckScale()
|
||||
local owner = self:GetOwner()
|
||||
if not owner:IsValid() then return end
|
||||
|
||||
-- RenderMultiply doesn't work with this..
|
||||
if self.BoneMerge and owner:GetBoneCount() and owner:GetBoneCount() > 1 then
|
||||
if self.Scale * self.Size ~= NORMAL then
|
||||
if not self.requires_bone_model_scale then
|
||||
self.requires_bone_model_scale = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
self.requires_bone_model_scale = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetAlternativeScaling(b)
|
||||
self.AlternativeScaling = b
|
||||
self:SetScale(self.Scale)
|
||||
end
|
||||
|
||||
function PART:SetScale(vec)
|
||||
local max_scale = GetConVar("pac_model_max_scales"):GetFloat()
|
||||
local largest_scale = math.max(math.abs(vec.x), math.abs(vec.y), math.abs(vec.z))
|
||||
|
||||
if vec and max_scale > 0 and (LocalPlayer() ~= self:GetPlayerOwner()) then --clamp for other players if they have pac_model_max_scales convar more than 0
|
||||
vec = Vector(math.Clamp(vec.x, -max_scale, max_scale), math.Clamp(vec.y, -max_scale, max_scale), math.Clamp(vec.z, -max_scale, max_scale))
|
||||
end
|
||||
if largest_scale > 10000 then --warn about the default max scale
|
||||
self:SetError("Scale is being limited due to having an excessive component. Default maximum values are 10000")
|
||||
else self:SetError() end --if ok, clear the warning
|
||||
vec = vec or Vector(1,1,1)
|
||||
|
||||
self.Scale = vec
|
||||
|
||||
if not self:CheckScale() then
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
end
|
||||
|
||||
local vec_one = Vector(1,1,1)
|
||||
|
||||
function PART:ApplyMatrix()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
local mat = Matrix()
|
||||
|
||||
if self.ClassName ~= "model2" then
|
||||
mat:Translate(self.Position + self.PositionOffset)
|
||||
mat:Rotate(self.Angles + self.AngleOffset)
|
||||
end
|
||||
|
||||
if ent:IsPlayer() or ent:IsNPC() then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "size", ent, self.Size, {
|
||||
StandingHullHeight = self.StandingHullHeight,
|
||||
CrouchingHullHeight = self.CrouchingHullHeight,
|
||||
HullWidth = self.HullWidth,
|
||||
})
|
||||
|
||||
if self.Size == 1 and self.Scale == vec_one then
|
||||
if self.InverseKinematics then
|
||||
if ent:GetModelScale() ~= 1 then
|
||||
ent:SetModelScale(1, 0)
|
||||
end
|
||||
ent:SetIK(true)
|
||||
else
|
||||
ent:SetModelScale(1.000001, 0)
|
||||
ent:SetIK(false)
|
||||
end
|
||||
end
|
||||
|
||||
mat:Scale(self.Scale)
|
||||
else
|
||||
mat:Scale(self.Scale * self.Size)
|
||||
end
|
||||
|
||||
ent.pac_model_scale = mat:GetScale()
|
||||
|
||||
if mat:IsIdentity() then
|
||||
ent:DisableMatrix("RenderMultiply")
|
||||
else
|
||||
ent:EnableMatrix("RenderMultiply", mat)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSize(var)
|
||||
var = var or 1
|
||||
|
||||
self.Size = var
|
||||
|
||||
if not self:CheckScale() then
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:CheckBoneMerge()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent == pac.LocalHands or ent == pac.LocalViewModel then return end
|
||||
|
||||
if self.skip_orient then return end
|
||||
|
||||
if ent:IsValid() and not ent:IsPlayer() and ent:GetModel() then
|
||||
if self.BoneMerge then
|
||||
local owner = self:GetParentOwner()
|
||||
|
||||
if ent:GetParent() ~= owner then
|
||||
ent:SetParent(owner)
|
||||
|
||||
if not ent:IsEffectActive(EF_BONEMERGE) then
|
||||
ent:AddEffects(EF_BONEMERGE)
|
||||
owner.pac_bonemerged = owner.pac_bonemerged or {}
|
||||
table.insert(owner.pac_bonemerged, ent)
|
||||
ent.RenderOverride = function()
|
||||
ent.pac_drawing_model = true
|
||||
ent:DrawModel()
|
||||
ent.pac_drawing_model = false
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if ent:GetParent():IsValid() then
|
||||
local owner = ent:GetParent()
|
||||
ent:SetParent(NULL)
|
||||
|
||||
if ent:IsEffectActive(EF_BONEMERGE) then
|
||||
ent:RemoveEffects(EF_BONEMERGE)
|
||||
ent.RenderOverride = nil
|
||||
|
||||
if owner:IsValid() then
|
||||
owner.pac_bonemerged = owner.pac_bonemerged or {}
|
||||
for i, v in ipairs(owner.pac_bonemerged) do
|
||||
if v == ent then
|
||||
table.remove(owner.pac_bonemerged, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.requires_bone_model_scale = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnBuildBonePositions()
|
||||
if self.AlternativeScaling then return end
|
||||
|
||||
local ent = self:GetOwner()
|
||||
local owner = self:GetParentOwner()
|
||||
|
||||
if not ent:IsValid() or not owner:IsValid() or not ent:GetBoneCount() or ent:GetBoneCount() < 1 then return end
|
||||
|
||||
if self.requires_bone_model_scale then
|
||||
local scale = self.Scale * self.Size
|
||||
|
||||
for i = 0, ent:GetBoneCount() - 1 do
|
||||
if i == 0 then
|
||||
ent:ManipulateBoneScale(i, ent:GetManipulateBoneScale(i) * Vector(scale.x ^ 0.25, scale.y ^ 0.25, scale.z ^ 0.25))
|
||||
else
|
||||
ent:ManipulateBonePosition(i, ent:GetManipulateBonePosition(i) + Vector((scale.x-1) ^ 4, 0, 0))
|
||||
ent:ManipulateBoneScale(i, ent:GetManipulateBoneScale(i) * scale)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
include("model/entity.lua")
|
||||
include("model/weapon.lua")
|
||||
262
lua/pac3/core/client/parts/model/entity.lua
Normal file
262
lua/pac3/core/client/parts/model/entity.lua
Normal file
@@ -0,0 +1,262 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("model2")
|
||||
|
||||
PART.FriendlyName = "entity"
|
||||
PART.ClassName = "entity2"
|
||||
PART.Category = "entity"
|
||||
PART.ManualDraw = true
|
||||
PART.HandleModifiersManually = true
|
||||
PART.Icon = 'icon16/brick.png'
|
||||
PART.Group = "entity"
|
||||
PART.is_entity_part = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:SetPropertyGroup("generic")
|
||||
:PropertyOrder("Name")
|
||||
:PropertyOrder("Hide")
|
||||
:PropertyOrder("ParentName")
|
||||
:SetPropertyGroup("appearance")
|
||||
:GetSet("NoDraw", false)
|
||||
:GetSet("DrawShadow", true)
|
||||
:GetSet("InverseKinematics", true)
|
||||
|
||||
:SetPropertyGroup("hull")
|
||||
:GetSet("StandingHullHeight", 72, {editor_panel = "hull"})
|
||||
:GetSet("CrouchingHullHeight", 36, {editor_panel = "hull", crouch = true})
|
||||
:GetSet("HullWidth", 32, {editor_panel = "hull"})
|
||||
:EndStorableVars()
|
||||
|
||||
BUILDER:RemoveProperty("BoneMerge")
|
||||
BUILDER:RemoveProperty("Bone")
|
||||
BUILDER:RemoveProperty("EyeAngles")
|
||||
BUILDER:RemoveProperty("AimPartName")
|
||||
BUILDER:RemoveProperty("ForceObjUrl")
|
||||
|
||||
function PART:SetDrawShadow(b)
|
||||
self.DrawShadow = b
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent:DrawShadow(b)
|
||||
ent:MarkShadowAsDirty()
|
||||
end
|
||||
|
||||
function PART:SetStandingHullHeight(val)
|
||||
self.StandingHullHeight = val
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
function PART:SetCrouchingHullHeight(val)
|
||||
self.CrouchingHullHeight = val
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
function PART:SetHullWidth(val)
|
||||
self.HullWidth = val
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName(("/" .. self:GetModel()):match(".+/(.-)%.")) or self:GetModel()
|
||||
|
||||
local class_name = "NULL"
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
class_name = ent:GetClass()
|
||||
end
|
||||
|
||||
return (str and str:gsub("%d", "") or "error") .. " " .. class_name .. " model"
|
||||
end
|
||||
|
||||
function PART:SetPosition(pos)
|
||||
self.Position = pos
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:SetAngles(ang)
|
||||
self.Angles = ang
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:SetPositionOffset(pos)
|
||||
self.PositionOffset = pos
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:SetAngleOffset(ang)
|
||||
self.AngleOffset = ang
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:GetBonePosition()
|
||||
local ent = self:GetParentOwner()
|
||||
if not ent:IsValid() then return Vector(), Angle() end
|
||||
local ang = ent:GetAngles()
|
||||
if ent:IsPlayer() then
|
||||
ang.p = 0
|
||||
end
|
||||
return ent:GetPos(), ang
|
||||
end
|
||||
|
||||
-- this also implicitly overrides parent init to not create a custom owner
|
||||
function PART:Initialize()
|
||||
self.material_count = 0
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local ent = self:GetOwner()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
self:PreEntityDraw(ent, pos, ang)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
self:PostEntityDraw(ent, pos, ang)
|
||||
end
|
||||
|
||||
local temp_mat = Material( "models/error/new light1" )
|
||||
|
||||
function PART:RenderOverride(ent)
|
||||
-- if the draw call is not from pac don't bother
|
||||
if not ent.pac_drawing_model then
|
||||
if not ent.pac_is_drawing and ent ~= pac.LocalPlayer and ent.pac_ragdoll_owner ~= pac.LocalPlayer then
|
||||
ent.RenderOverride = nil
|
||||
ent:DisableMatrix("RenderMultiply")
|
||||
ent:SetSkin(0)
|
||||
ent:SetLOD(-1)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self:IsValid() and self:GetParentOwner():IsValid() then
|
||||
if ent.pac_bonemerged then
|
||||
for _, e in ipairs(ent.pac_bonemerged) do
|
||||
if e.pac_drawing_model then return end
|
||||
end
|
||||
end
|
||||
|
||||
-- so eyes work
|
||||
if self.NoDraw then
|
||||
if ent == pac.LocalViewModel or ent == pac.LocalHands then return end
|
||||
render.SetBlend(0)
|
||||
render.ModelMaterialOverride(temp_mat)
|
||||
ent.pac_drawing_model = true
|
||||
ent:DrawModel()
|
||||
ent.pac_drawing_model = false
|
||||
render.SetBlend(1)
|
||||
render.ModelMaterialOverride()
|
||||
return
|
||||
end
|
||||
|
||||
ent:SetSkin(self:GetSkin())
|
||||
self:Draw(self.Translucent and "translucent" or "opaque")
|
||||
else
|
||||
ent.RenderOverride = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
function ent.RenderOverride()
|
||||
if self:IsValid() then
|
||||
self:RenderOverride(ent)
|
||||
else
|
||||
ent.RenderOverride = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not self.real_model then
|
||||
self.real_model = ent:GetModel()
|
||||
end
|
||||
|
||||
if not (self.old_model == self:GetModel()) or
|
||||
(pac.LocalHands:IsValid() and ent == pac.LocalHands
|
||||
and not (self.real_model == pac.LocalHands:GetModel())) then
|
||||
self.old_model = self:GetModel()
|
||||
self:SetModel(self:GetModel())
|
||||
end
|
||||
|
||||
self:SetDrawShadow(self:GetDrawShadow())
|
||||
self:RefreshModel()
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetParentOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.RenderOverride = nil
|
||||
ent:DisableMatrix("RenderMultiply")
|
||||
ent:SetSkin(0)
|
||||
ent:SetLOD(-1)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:RealSetModel(path)
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent:SetModel(path)
|
||||
self.real_model = path
|
||||
self:RefreshModel()
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
local player_owner = self:GetPlayerOwner()
|
||||
|
||||
pac.emut.RestoreMutations(player_owner, "model", ent)
|
||||
|
||||
if ent:IsPlayer() or ent:IsNPC() then
|
||||
pac.emut.RestoreMutations(player_owner, "size", ent)
|
||||
end
|
||||
|
||||
ent:DisableMatrix("RenderMultiply")
|
||||
end
|
||||
|
||||
function PART:SetInverseKinematics(b)
|
||||
self.InverseKinematics = b
|
||||
|
||||
local ent = self:GetParentOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.pac_enable_ik = b
|
||||
self:ApplyMatrix()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
self:CheckBoneMerge()
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
local model = ent:GetModel()
|
||||
local bone_count = ent:GetBoneCount()
|
||||
if
|
||||
self.last_model ~= model or
|
||||
self.last_bone_count ~= bone_count
|
||||
then
|
||||
self:RefreshModel()
|
||||
self.last_model = model
|
||||
self.last_bone_count = bone_count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
171
lua/pac3/core/client/parts/model/weapon.lua
Normal file
171
lua/pac3/core/client/parts/model/weapon.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
--[[
|
||||
| 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 pac = pac
|
||||
local NULL = NULL
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("model2")
|
||||
|
||||
PART.ClassName = "weapon"
|
||||
PART.FriendlyName = "weapon"
|
||||
PART.Category = "entity"
|
||||
PART.ManualDraw = true
|
||||
PART.HandleModifiersManually = true
|
||||
PART.Icon = 'icon16/brick.png'
|
||||
PART.Group = "entity"
|
||||
PART.is_model_part = false
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:SetPropertyGroup("generic")
|
||||
:PropertyOrder("Name")
|
||||
:PropertyOrder("Hide")
|
||||
:PropertyOrder("ParentName")
|
||||
:GetSet("OverridePosition", false)
|
||||
:GetSet("Class", "all", {enums = function()
|
||||
local out = {
|
||||
["physgun"] = "weapon_physgun",
|
||||
["357"] = "weapon_357",
|
||||
["alyxgun"] = "weapon_alyxgun",
|
||||
["annabelle"] = "weapon_annabelle",
|
||||
["ar2"] = "weapon_ar2",
|
||||
["brickbat"] = "weapon_brickbat",
|
||||
["bugbait"] = "weapon_bugbait",
|
||||
["crossbow"] = "weapon_crossbow",
|
||||
["crowbar"] = "weapon_crowbar",
|
||||
["frag"] = "weapon_frag",
|
||||
["physcannon"] = "weapon_physcannon",
|
||||
["pistol"] = "weapon_pistol",
|
||||
["rpg"] = "weapon_rpg",
|
||||
["shotgun"] = "weapon_shotgun",
|
||||
["smg1"] = "weapon_smg1",
|
||||
["striderbuster"] = "weapon_striderbuster",
|
||||
["stunstick"] = "weapon_stunstick",
|
||||
}
|
||||
for _, tbl in pairs(weapons.GetList()) do
|
||||
if not tbl.ClassName:StartWith("ai_") then
|
||||
local friendly = tbl.ClassName:match("weapon_(.+)") or tbl.ClassName
|
||||
out[friendly] = tbl.ClassName
|
||||
end
|
||||
end
|
||||
return out
|
||||
end})
|
||||
:SetPropertyGroup("appearance")
|
||||
:GetSet("NoDraw", false)
|
||||
:GetSet("DrawShadow", true)
|
||||
:SetPropertyGroup("orientation")
|
||||
:GetSet("Bone", "right hand")
|
||||
:EndStorableVars()
|
||||
|
||||
BUILDER:RemoveProperty("Model")
|
||||
BUILDER:RemoveProperty("ForceObjUrl")
|
||||
|
||||
function PART:SetDrawShadow(b)
|
||||
self.DrawShadow = b
|
||||
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
ent:DrawShadow(b)
|
||||
ent:MarkShadowAsDirty()
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
if self.Class ~= "all" then
|
||||
return self.Class
|
||||
end
|
||||
return self.ClassName
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self.material_count = 0
|
||||
end
|
||||
function PART:OnDraw()
|
||||
local ent = self:GetOwner()
|
||||
if not ent:IsValid() then return end
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
local old
|
||||
if self.OverridePosition then
|
||||
old = ent:GetParent()
|
||||
ent:SetParent(NULL)
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
pac.SetupBones(ent)
|
||||
end
|
||||
ent.pac_render = true
|
||||
|
||||
self:PreEntityDraw(ent, pos, ang)
|
||||
self:DrawModel(ent, pos, ang)
|
||||
self:PostEntityDraw(ent, pos, ang)
|
||||
pac.ResetBones(ent)
|
||||
|
||||
if self.OverridePosition then
|
||||
ent:MarkShadowAsDirty()
|
||||
ent:SetParent(old)
|
||||
end
|
||||
ent.pac_render = nil
|
||||
end
|
||||
|
||||
PART.AlwaysThink = true
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
if ent:IsValid() and ent.GetActiveWeapon then
|
||||
local wep = ent:GetActiveWeapon()
|
||||
if wep:IsValid() then
|
||||
if wep ~= self.Owner then
|
||||
if self.Class == "all" or (self.Class:lower() == wep:GetClass():lower()) then
|
||||
self:OnHide()
|
||||
self.Owner = wep
|
||||
self:SetEventTrigger(self, false)
|
||||
wep.RenderOverride = function()
|
||||
if self:IsHiddenCached() then
|
||||
wep.RenderOverride = nil
|
||||
return
|
||||
end
|
||||
if wep.pac_render then
|
||||
if not self.NoDraw then
|
||||
if self.DrawShadow then
|
||||
wep:CreateShadow()
|
||||
end
|
||||
wep:DrawModel()
|
||||
end
|
||||
end
|
||||
end
|
||||
wep.pac_weapon_part = self
|
||||
self:SetDrawShadow(self:GetDrawShadow())
|
||||
else
|
||||
self:SetEventTrigger(self, true)
|
||||
self:OnHide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
self.Owner = NULL
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
|
||||
if ent:IsValid() and ent.GetActiveWeapon then
|
||||
for _, wep in pairs(ent:GetWeapons()) do
|
||||
if wep.pac_weapon_part == self then
|
||||
wep.RenderOverride = nil
|
||||
wep:SetParent(ent)
|
||||
end
|
||||
end
|
||||
self.Owner = NULL
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
108
lua/pac3/core/client/parts/motion_blur.lua
Normal file
108
lua/pac3/core/client/parts/motion_blur.lua
Normal file
@@ -0,0 +1,108 @@
|
||||
--[[
|
||||
| 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 render_SetBlend = render.SetBlend
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "motion_blur"
|
||||
PART.Group = 'modifiers'
|
||||
PART.Icon = 'icon16/shape_ungroup.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:GetSet("Bone", "none")
|
||||
:GetSet("Alpha", 0.5)
|
||||
:GetSet("BlurLength", 10)
|
||||
:GetSet("BlurSpacing", 0.1)
|
||||
:EndStorableVars()
|
||||
|
||||
|
||||
function PART:OnShow()
|
||||
if self.BlurLength > 0 then
|
||||
self.blur_history = {}
|
||||
self.blur_last_add = 0
|
||||
pac.drawing_motionblur_alpha = false
|
||||
end
|
||||
end
|
||||
|
||||
function PART:DrawBlur(pos, ang)
|
||||
local parent = self:GetParent()
|
||||
if not parent:IsValid() then return end
|
||||
local ent = parent:GetOwner()
|
||||
|
||||
if not parent.OnDraw then return end
|
||||
|
||||
self.blur_history = self.blur_history or {}
|
||||
|
||||
local blurSpacing = self.BlurSpacing
|
||||
|
||||
if not self.blur_last_add or blurSpacing == 0 or self.blur_last_add < pac.RealTime then
|
||||
|
||||
local bones = {}
|
||||
local i = 1
|
||||
for id = 0, ent:GetBoneCount() - 1 do
|
||||
local mat = ent:GetBoneMatrix(id)
|
||||
if mat then
|
||||
bones[i] = {id = id, matrix = mat}
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
table_insert(self.blur_history, {pos, ang, ent:GetCycle(), bones})
|
||||
self.blur_last_add = pac.RealTime + blurSpacing / 1000
|
||||
end
|
||||
|
||||
local prev_cycle = ent:GetCycle()
|
||||
|
||||
local blurHistoryLength = #self.blur_history
|
||||
for i = 1, blurHistoryLength do
|
||||
local pos, ang, cycle, bones = self.blur_history[i][1], self.blur_history[i][2], self.blur_history[i][3], self.blur_history[i][4]
|
||||
|
||||
local alpha = self.Alpha * (i / blurHistoryLength)
|
||||
render_SetBlend(alpha)
|
||||
|
||||
if ent then
|
||||
ent:SetCycle(cycle)
|
||||
|
||||
for _, data in ipairs(bones) do
|
||||
pcall(ent.SetBoneMatrix, ent, data.id, data.matrix)
|
||||
end
|
||||
end
|
||||
|
||||
pac.drawing_motionblur_alpha = alpha
|
||||
parent:OnDraw(ent, pos, ang)
|
||||
pac.drawing_motionblur_alpha = false
|
||||
|
||||
if ent then
|
||||
pac.SetupBones(ent)
|
||||
end
|
||||
end
|
||||
|
||||
ent:SetCycle(prev_cycle)
|
||||
|
||||
local maximumBlurHistoryLength = math.min(self.BlurLength, 20)
|
||||
while #self.blur_history >= maximumBlurHistoryLength do
|
||||
table_remove(self.blur_history, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if pac.drawing_motionblur_alpha then return end
|
||||
|
||||
if self.BlurLength > 0 then
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
|
||||
self:DrawBlur(pos, ang)
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
129
lua/pac3/core/client/parts/movement.lua
Normal file
129
lua/pac3/core/client/parts/movement.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "player_movement"
|
||||
PART.Group = "entity"
|
||||
PART.Icon = "icon16/user_go.png"
|
||||
|
||||
|
||||
local pac_movement_default = {}
|
||||
local update_these = {}
|
||||
|
||||
local function ADD(PART, name, default, ...)
|
||||
BUILDER:GetSet(name, default, ...)
|
||||
|
||||
pac_movement_default[name] = default
|
||||
|
||||
PART["Set" .. name] = function(self, val)
|
||||
self[name] = val
|
||||
|
||||
local ply = self:GetRootPart():GetOwner()
|
||||
|
||||
if ply == pac.LocalPlayer then
|
||||
|
||||
if self:IsHidden() then return end
|
||||
|
||||
local num = GetConVarNumber("pac_free_movement")
|
||||
if num == 1 or (num == -1 and hook.Run("PlayerNoClip", ply, true)) then
|
||||
ply.pac_movement = ply.pac_movement or table.Copy(pac_movement_default)
|
||||
|
||||
if ply.pac_movement[name] ~= val then
|
||||
net.Start("pac_modify_movement", true)
|
||||
net.WriteString(name)
|
||||
net.WriteType(val)
|
||||
net.SendToServer()
|
||||
end
|
||||
ply.pac_movement[name] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(update_these, function(s) PART["Set" .. name](s, PART["Get" .. name](s)) end)
|
||||
end
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
ADD(PART, "Noclip", false)
|
||||
ADD(PART, "Gravity", Vector(0, 0, -600))
|
||||
|
||||
BUILDER:SetPropertyGroup("movement")
|
||||
ADD(PART, "SprintSpeed", 400)
|
||||
ADD(PART, "RunSpeed", 200)
|
||||
ADD(PART, "WalkSpeed", 100)
|
||||
ADD(PART, "DuckSpeed", 25)
|
||||
|
||||
BUILDER:SetPropertyGroup("ground")
|
||||
ADD(PART, "JumpHeight", 200, {editor_clamp = {0, 10000}})
|
||||
ADD(PART, "MaxGroundSpeed", 750)
|
||||
ADD(PART, "StickToGround", true)
|
||||
ADD(PART, "GroundFriction", 0.12, {editor_clamp = {0, 1}, editor_sensitivity = 0.1})
|
||||
|
||||
BUILDER:SetPropertyGroup("air")
|
||||
ADD(PART, "AllowZVelocity", false)
|
||||
ADD(PART, "AirFriction", 0.01, {editor_clamp = {0, 1}, editor_sensitivity = 0.1})
|
||||
ADD(PART, "MaxAirSpeed", 1)
|
||||
|
||||
BUILDER:SetPropertyGroup("view angles")
|
||||
ADD(PART, "ReversePitch", false)
|
||||
ADD(PART, "UnlockPitch", false)
|
||||
ADD(PART, "VelocityToViewAngles", 0, {editor_clamp = {0, 1}, editor_sensitivity = 0.1})
|
||||
ADD(PART, "RollAmount", 0, {editor_sensitivity = 0.25})
|
||||
|
||||
BUILDER:SetPropertyGroup("fin")
|
||||
ADD(PART, "FinEfficiency", 0)
|
||||
ADD(PART, "FinLiftMode", "normal", {enums = {
|
||||
normal = "normal",
|
||||
none = "none",
|
||||
}})
|
||||
ADD(PART, "FinCline", false)
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
local str = self.ClassName
|
||||
|
||||
if ent:IsValid() then
|
||||
if ent:IsPlayer() then
|
||||
str = ent:Nick()
|
||||
else
|
||||
str = language.GetPhrase(ent:GetClass())
|
||||
end
|
||||
end
|
||||
|
||||
return str .. "'s movement"
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.last_movement_part = self:GetUniqueID()
|
||||
for i,v in ipairs(update_these) do
|
||||
v(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
|
||||
if ent == pac.LocalPlayer and ent.last_movement_part == self:GetUniqueID() then
|
||||
net.Start("pac_modify_movement", true)
|
||||
net.WriteString("disable")
|
||||
net.SendToServer()
|
||||
|
||||
ent.pac_movement = nil
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
375
lua/pac3/core/client/parts/particles.lua
Normal file
375
lua/pac3/core/client/parts/particles.lua
Normal file
@@ -0,0 +1,375 @@
|
||||
--[[
|
||||
| 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 cam_IgnoreZ = cam.IgnoreZ
|
||||
local vector_origin = vector_origin
|
||||
local FrameTime = FrameTime
|
||||
local angle_origin = Angle(0,0,0)
|
||||
local WorldToLocal = WorldToLocal
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "particles"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/water.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:PropertyOrder("Name")
|
||||
BUILDER:PropertyOrder("Hide")
|
||||
BUILDER:PropertyOrder("ParentName")
|
||||
BUILDER:GetSet("Follow", false)
|
||||
BUILDER:GetSet("Additive", false)
|
||||
BUILDER:GetSet("FireOnce", false)
|
||||
BUILDER:GetSet("FireDelay", 0.2)
|
||||
BUILDER:GetSet("NumberParticles", 1)
|
||||
BUILDER:GetSet("PositionSpread", 0)
|
||||
BUILDER:GetSet("PositionSpread2", Vector(0,0,0))
|
||||
BUILDER:GetSet("DieTime", 3)
|
||||
BUILDER:GetSet("StartSize", 2)
|
||||
BUILDER:GetSet("EndSize", 20)
|
||||
BUILDER:GetSet("StartLength", 0)
|
||||
BUILDER:GetSet("EndLength", 0)
|
||||
BUILDER:GetSet("ParticleAngle", Angle(0,0,0))
|
||||
BUILDER:GetSet("AddFrametimeLife", false)
|
||||
BUILDER:SetPropertyGroup("stick")
|
||||
BUILDER:GetSet("AlignToSurface", true)
|
||||
BUILDER:GetSet("StickToSurface", true)
|
||||
BUILDER:GetSet("StickLifetime", 2)
|
||||
BUILDER:GetSet("StickStartSize", 20)
|
||||
BUILDER:GetSet("StickEndSize", 0)
|
||||
BUILDER:GetSet("StickStartAlpha", 255)
|
||||
BUILDER:GetSet("StickEndAlpha", 0)
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Material", "effects/slime1")
|
||||
BUILDER:GetSet("StartAlpha", 255)
|
||||
BUILDER:GetSet("EndAlpha", 0)
|
||||
BUILDER:GetSet("Translucent", true)
|
||||
BUILDER:GetSet("Color2", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Color1", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("RandomColor", false)
|
||||
BUILDER:GetSet("Lighting", true)
|
||||
BUILDER:GetSet("3D", false)
|
||||
BUILDER:GetSet("DoubleSided", true)
|
||||
BUILDER:GetSet("DrawManual", false)
|
||||
BUILDER:SetPropertyGroup("rotation")
|
||||
BUILDER:GetSet("ZeroAngle",true)
|
||||
BUILDER:GetSet("RandomRollSpeed", 0)
|
||||
BUILDER:GetSet("RollDelta", 0)
|
||||
BUILDER:GetSet("ParticleAngleVelocity", Vector(50, 50, 50))
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:SetPropertyGroup("movement")
|
||||
BUILDER:GetSet("Velocity", 250)
|
||||
BUILDER:GetSet("Spread", 0.1)
|
||||
BUILDER:GetSet("AirResistance", 5)
|
||||
BUILDER:GetSet("Bounce", 5)
|
||||
BUILDER:GetSet("Gravity", Vector(0,0, -50))
|
||||
BUILDER:GetSet("Collide", true)
|
||||
BUILDER:GetSet("Sliding", true)
|
||||
--BUILDER:GetSet("AddVelocityFromOwner", false)
|
||||
BUILDER:GetSet("OwnerVelocityMultiplier", 0)
|
||||
|
||||
|
||||
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
return pac.PrettifyName(("/".. self:GetMaterial()):match(".+/(.+)")) or "error"
|
||||
end
|
||||
|
||||
local function RemoveCallback(particle)
|
||||
particle:SetLifeTime(0)
|
||||
particle:SetDieTime(0)
|
||||
|
||||
particle:SetStartSize(0)
|
||||
particle:SetEndSize(0)
|
||||
|
||||
particle:SetStartAlpha(0)
|
||||
particle:SetEndAlpha(0)
|
||||
end
|
||||
|
||||
local function SlideCallback(particle, hitpos, normal)
|
||||
particle:SetBounce(1)
|
||||
local vel = particle:GetVelocity()
|
||||
vel.z = 0
|
||||
particle:SetVelocity(vel)
|
||||
particle:SetPos(hitpos + normal)
|
||||
end
|
||||
|
||||
local function StickCallback(particle, hitpos, normal)
|
||||
particle:SetAngleVelocity(Angle(0, 0, 0))
|
||||
|
||||
if particle.Align then
|
||||
local ang = normal:Angle()
|
||||
ang:RotateAroundAxis(normal, particle:GetAngles().y)
|
||||
particle:SetAngles(ang)
|
||||
end
|
||||
|
||||
if particle.Stick then
|
||||
particle:SetVelocity(Vector(0, 0, 0))
|
||||
particle:SetGravity(Vector(0, 0, 0))
|
||||
end
|
||||
|
||||
particle:SetLifeTime(0)
|
||||
particle:SetDieTime(particle.StickLifeTime or 0)
|
||||
|
||||
particle:SetStartSize(particle.StickStartSize or 0)
|
||||
particle:SetEndSize(particle.StickEndSize or 0)
|
||||
|
||||
particle:SetStartAlpha(particle.StickStartAlpha or 0)
|
||||
particle:SetEndAlpha(particle.StickEndAlpha or 0)
|
||||
end
|
||||
|
||||
function PART:GetEmitter()
|
||||
if not self.emitter then
|
||||
self.NextShot = 0
|
||||
self.Created = pac.RealTime + 0.1
|
||||
self.emitter = ParticleEmitter(vector_origin, self:Get3D())
|
||||
end
|
||||
|
||||
return self.emitter
|
||||
end
|
||||
|
||||
function PART:SetDrawManual(b)
|
||||
self.DrawManual = b
|
||||
self:GetEmitter():SetNoDraw(b)
|
||||
end
|
||||
|
||||
function PART:SetNumberParticles(num)
|
||||
self.NumberParticles = math.Clamp(num, 0, 100)
|
||||
end
|
||||
|
||||
function PART:Set3D(b)
|
||||
self["3D"] = b
|
||||
self.emitter = nil
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
self.CanKeepFiring = true
|
||||
self.FirstShot = true
|
||||
if not from_rendering then
|
||||
self.NextShot = 0
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
self:EmitParticles(self.Follow and vector_origin or pos, self.Follow and angle_origin or ang, ang)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if not self.FireOnce then self.CanKeepFiring = true end
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
local emitter = self:GetEmitter()
|
||||
|
||||
emitter:SetPos(pos)
|
||||
if self.DrawManual or self.IgnoreZ or self.Follow or self.BlendMode ~= "" then
|
||||
|
||||
if not self.nodraw then
|
||||
emitter:SetNoDraw(true)
|
||||
self.nodraw = true
|
||||
end
|
||||
|
||||
if self.Follow then
|
||||
cam.Start3D(WorldToLocal(EyePos(), EyeAngles(), pos, ang))
|
||||
if self.IgnoreZ then cam.IgnoreZ(true) end
|
||||
emitter:Draw()
|
||||
if self.IgnoreZ then cam.IgnoreZ(false) end
|
||||
cam.End3D()
|
||||
else
|
||||
emitter:Draw()
|
||||
end
|
||||
else
|
||||
if self.nodraw then
|
||||
self:SetDrawManual(self:GetDrawManual())
|
||||
self.nodraw = false
|
||||
end
|
||||
end
|
||||
self:EmitParticles(self.Follow and vector_origin or pos, self.Follow and angle_origin or ang, ang)
|
||||
end
|
||||
|
||||
function PART:SetAdditive(b)
|
||||
self.Additive = b
|
||||
|
||||
self:SetMaterial(self:GetMaterial())
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var, function(mat)
|
||||
mat:SetFloat("$alpha", 0.999)
|
||||
mat:SetInt("$spriterendermode", self.Additive and 5 or 1)
|
||||
self.Materialm = mat
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end, "Sprite") then
|
||||
if var == "" then
|
||||
self.Materialm = nil
|
||||
else
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
self.Material = var
|
||||
end
|
||||
|
||||
function PART:EmitParticles(pos, ang, real_ang)
|
||||
if self.FireOnce and not self.FirstShot then self.CanKeepFiring = false end
|
||||
local emt = self:GetEmitter()
|
||||
if not emt then return end
|
||||
|
||||
if self.NextShot < pac.RealTime and self.CanKeepFiring then
|
||||
if self.Material == "" then return end
|
||||
if self.Velocity == 500.01 then return end
|
||||
|
||||
local originalAng = ang
|
||||
ang = ang:Forward()
|
||||
|
||||
local double = 1
|
||||
if self.DoubleSided then
|
||||
double = 2
|
||||
end
|
||||
|
||||
for _ = 1, self.NumberParticles do
|
||||
local mats = self.Material:Split(";")
|
||||
if #mats > 1 then
|
||||
self.Materialm = pac.Material(table.Random(mats), self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
local vec = Vector()
|
||||
|
||||
if self.Spread ~= 0 then
|
||||
vec = Vector(
|
||||
math.sin(math.Rand(0, 360)) * math.Rand(-self.Spread, self.Spread),
|
||||
math.cos(math.Rand(0, 360)) * math.Rand(-self.Spread, self.Spread),
|
||||
math.sin(math.random()) * math.Rand(-self.Spread, self.Spread)
|
||||
)
|
||||
end
|
||||
|
||||
local color
|
||||
|
||||
if self.RandomColor then
|
||||
color =
|
||||
{
|
||||
math.random(math.min(self.Color1.r, self.Color2.r), math.max(self.Color1.r, self.Color2.r)),
|
||||
math.random(math.min(self.Color1.g, self.Color2.g), math.max(self.Color1.g, self.Color2.g)),
|
||||
math.random(math.min(self.Color1.b, self.Color2.b), math.max(self.Color1.b, self.Color2.b))
|
||||
}
|
||||
else
|
||||
color = {self.Color1.r, self.Color1.g, self.Color1.b}
|
||||
end
|
||||
|
||||
local roll = math.Rand(-self.RollDelta, self.RollDelta)
|
||||
|
||||
if self.PositionSpread ~= 0 then
|
||||
pos = pos + Angle(math.Rand(-180, 180), math.Rand(-180, 180), math.Rand(-180, 180)):Forward() * self.PositionSpread
|
||||
end
|
||||
|
||||
do
|
||||
local vecAdd = Vector(
|
||||
math.Rand(-self.PositionSpread2.x, self.PositionSpread2.x),
|
||||
math.Rand(-self.PositionSpread2.x, self.PositionSpread2.y),
|
||||
math.Rand(-self.PositionSpread2.z, self.PositionSpread2.z)
|
||||
)
|
||||
vecAdd:Rotate(originalAng)
|
||||
pos = pos + vecAdd
|
||||
end
|
||||
|
||||
for i = 1, double do
|
||||
local particle = emt:Add(self.Materialm or self.Material, pos)
|
||||
|
||||
if double == 2 then
|
||||
local ang_
|
||||
if i == 1 then
|
||||
ang_ = (ang * -1):Angle()
|
||||
elseif i == 2 then
|
||||
ang_ = ang:Angle()
|
||||
end
|
||||
|
||||
particle:SetAngles(ang_)
|
||||
else
|
||||
particle:SetAngles(ang:Angle())
|
||||
end
|
||||
|
||||
if self.OwnerVelocityMultiplier ~= 0 then
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
if owner:IsValid() then
|
||||
vec = vec + (owner:GetVelocity() * self.OwnerVelocityMultiplier)
|
||||
end
|
||||
end
|
||||
|
||||
particle:SetVelocity((vec + ang) * self.Velocity)
|
||||
particle:SetColor(unpack(color))
|
||||
particle:SetColor(unpack(color))
|
||||
|
||||
local life = math.Clamp(self.DieTime, 0.0001, 50)
|
||||
if self.AddFrametimeLife then
|
||||
life = life + FrameTime()
|
||||
end
|
||||
particle:SetDieTime(life)
|
||||
|
||||
particle:SetStartAlpha(self.StartAlpha)
|
||||
particle:SetEndAlpha(self.EndAlpha)
|
||||
particle:SetStartSize(self.StartSize)
|
||||
particle:SetEndSize(self.EndSize)
|
||||
particle:SetStartLength(self.StartLength)
|
||||
particle:SetEndLength(self.EndLength)
|
||||
|
||||
if self.RandomRollSpeed ~= 0 then
|
||||
particle:SetRoll(self.RandomRollSpeed * 36)
|
||||
end
|
||||
|
||||
if self.RollDelta ~= 0 then
|
||||
particle:SetRollDelta(self.RollDelta + roll)
|
||||
end
|
||||
|
||||
particle:SetAirResistance(self.AirResistance)
|
||||
particle:SetBounce(self.Bounce)
|
||||
particle:SetGravity(self.Gravity)
|
||||
if self.ZeroAngle then particle:SetAngles(Angle(0,0,0))
|
||||
else particle:SetAngles(particle:GetAngles() + self.ParticleAngle) end
|
||||
particle:SetLighting(self.Lighting)
|
||||
|
||||
if not self.Follow then
|
||||
particle:SetCollide(self.Collide)
|
||||
end
|
||||
|
||||
if self.Sliding then
|
||||
particle:SetCollideCallback(SlideCallback)
|
||||
end
|
||||
|
||||
if self["3D"] then
|
||||
if not self.Sliding then
|
||||
if i == 1 then
|
||||
particle:SetCollideCallback(RemoveCallback)
|
||||
else
|
||||
particle:SetCollideCallback(StickCallback)
|
||||
end
|
||||
end
|
||||
|
||||
particle:SetAngleVelocity(Angle(self.ParticleAngleVelocity.x, self.ParticleAngleVelocity.y, self.ParticleAngleVelocity.z))
|
||||
|
||||
particle.Align = self.Align
|
||||
particle.Stick = self.Stick
|
||||
particle.StickLifeTime = self.StickLifeTime
|
||||
particle.StickStartSize = self.StickStartSize
|
||||
particle.StickEndSize = self.StickEndSize
|
||||
particle.StickStartAlpha = self.StickStartAlpha
|
||||
particle.StickEndAlpha = self.StickEndAlpha
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self.NextShot = pac.RealTime + self.FireDelay
|
||||
end
|
||||
self.FirstShot = false
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
237
lua/pac3/core/client/parts/physics.lua
Normal file
237
lua/pac3/core/client/parts/physics.lua
Normal file
@@ -0,0 +1,237 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.ClassName = "physics"
|
||||
|
||||
PART.Group = 'model'
|
||||
PART.Icon = 'icon16/shape_handles.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Box", true)
|
||||
BUILDER:GetSet("Radius", 1)
|
||||
BUILDER:GetSet("SelfCollision", false)
|
||||
BUILDER:GetSet("Gravity", true)
|
||||
BUILDER:GetSet("Collisions", true)
|
||||
BUILDER:GetSet("Mass", 100)
|
||||
|
||||
BUILDER:GetSet("Follow", false)
|
||||
|
||||
BUILDER:GetSet("SecondsToArrive", 0.1)
|
||||
|
||||
BUILDER:GetSet("MaxSpeed", 10000)
|
||||
BUILDER:GetSet("MaxAngular", 3600)
|
||||
|
||||
BUILDER:GetSet("MaxSpeedDamp", 1000)
|
||||
BUILDER:GetSet("MaxAngularDamp", 1000)
|
||||
BUILDER:GetSet("DampFactor", 1)
|
||||
|
||||
BUILDER:GetSet("ConstrainSphere", 0)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local function IsInvalidParent(self)
|
||||
return self.Parent.ClassName ~= "model" and self.Parent.ClassName ~= "model2"
|
||||
end
|
||||
|
||||
PART.phys = NULL
|
||||
|
||||
function PART:SetBox(b)
|
||||
self.Box = b
|
||||
self:SetRadius(self.Radius)
|
||||
end
|
||||
|
||||
function PART:SetCollisions(b)
|
||||
self.Collisions = b
|
||||
|
||||
if self.phys:IsValid() then
|
||||
self.phys:EnableCollisions(b)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMass(n)
|
||||
self.Mass = n
|
||||
|
||||
if self.phys:IsValid() then
|
||||
self.phys:SetMass(math.Clamp(n, 0.001, 50000))
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetRadius(n)
|
||||
self.Radius = n
|
||||
|
||||
if IsInvalidParent(self) then return end
|
||||
|
||||
local ent = self.Parent:GetOwner()
|
||||
|
||||
if n <= 0 then n = ent:BoundingRadius()/2 end
|
||||
|
||||
ent:SetNoDraw(false)
|
||||
|
||||
if self.Box then
|
||||
ent:PhysicsInitBox(Vector(1,1,1) * -n, Vector(1,1,1) * n)
|
||||
else
|
||||
ent:PhysicsInitSphere(n)
|
||||
end
|
||||
|
||||
self.phys = ent:GetPhysicsObject()
|
||||
|
||||
if self.Gravity ~= nil then
|
||||
self.phys:EnableGravity(self.Gravity)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetGravity(b)
|
||||
self.Gravity = b
|
||||
|
||||
if self.phys:IsValid() then
|
||||
self.phys:EnableGravity(b)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetSelfCollision(b)
|
||||
self.SelfCollision = b
|
||||
|
||||
if IsInvalidParent(self) then return end
|
||||
|
||||
local ent = self.Parent:GetOwner()
|
||||
|
||||
if b then
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_NONE)
|
||||
else
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
end
|
||||
end
|
||||
|
||||
local params = {}
|
||||
|
||||
function PART:OnThink()
|
||||
|
||||
if self.Parent.GetWorldPosition then
|
||||
if self.disabled then
|
||||
self:Enable()
|
||||
end
|
||||
else
|
||||
if not self.disabled then
|
||||
self:Disable()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local phys = self.phys
|
||||
|
||||
if phys:IsValid() then
|
||||
phys:Wake()
|
||||
|
||||
if self.Follow then
|
||||
params.pos = self.Parent:GetWorldPosition()
|
||||
params.angle = self.Parent:GetWorldAngles()
|
||||
|
||||
params.secondstoarrive = math.max(self.SecondsToArrive, 0.0001)
|
||||
params.maxangular = self.MaxAngular
|
||||
params.maxangulardamp = self.MaxAngularDamp
|
||||
params.maxspeed = self.MaxSpeed
|
||||
params.maxspeeddamp = self.MaxSpeedDamp
|
||||
params.dampfactor = self.DampFactor
|
||||
|
||||
params.teleportdistance = 0
|
||||
|
||||
phys:ComputeShadowControl(params)
|
||||
|
||||
|
||||
-- this is nicer i think
|
||||
if self.ConstrainSphere ~= 0 and phys:GetPos():Distance(self.Parent:GetWorldPosition()) > self.ConstrainSphere then
|
||||
phys:SetPos(self.Parent:GetWorldPosition() + (self.Parent:GetWorldPosition() - phys:GetPos()):GetNormalized() * -self.ConstrainSphere)
|
||||
end
|
||||
else
|
||||
if self.ConstrainSphere ~= 0 then
|
||||
local offset = self.Parent:GetWorldPosition() - phys:GetPos()
|
||||
|
||||
if offset:Length() > self.ConstrainSphere then
|
||||
phys:SetPos(self.Parent:GetWorldPosition() - offset:GetNormalized() * self.ConstrainSphere)
|
||||
phys:SetVelocity(Vector())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnParent(part)
|
||||
timer.Simple(0, function() self:Enable() end)
|
||||
end
|
||||
|
||||
function PART:OnUnParent(part)
|
||||
timer.Simple(0, function() self:Disable() end)
|
||||
end
|
||||
|
||||
|
||||
function PART:OnShow()
|
||||
timer.Simple(0, function() self:Enable() end)
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
timer.Simple(0, function() self:Disable() end)
|
||||
end
|
||||
|
||||
function PART:Enable()
|
||||
if IsInvalidParent(self) then return end
|
||||
|
||||
local part = self:GetParent()
|
||||
|
||||
part.skip_orient = true
|
||||
|
||||
local ent = part:GetOwner()
|
||||
ent:SetNoDraw(false)
|
||||
|
||||
self:SetRadius(self.Radius)
|
||||
|
||||
for key, val in pairs(self.StorableVars) do
|
||||
if pac.registered_parts.base.StorableVars[key] then goto CONTINUE end
|
||||
self["Set" .. key](self, self[key])
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
self.disabled = false
|
||||
end
|
||||
|
||||
function PART:Disable()
|
||||
if IsInvalidParent(self) then return end
|
||||
|
||||
local part = self:GetParent()
|
||||
|
||||
local ent = part:GetOwner()
|
||||
if ent:IsValid() then
|
||||
-- SetNoDraw does not care of validity but PhysicsInit does?
|
||||
ent:SetNoDraw(true)
|
||||
ent:PhysicsInit(SOLID_NONE)
|
||||
end
|
||||
part.skip_orient = false
|
||||
|
||||
self.disabled = true
|
||||
end
|
||||
|
||||
function PART:SetPositionDamping(num)
|
||||
self.PositionDamping = num
|
||||
|
||||
if self.phys:IsValid() then
|
||||
self.phys:SetDamping(self.PositionDamping, self.AngleDamping)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetAngleDamping(num)
|
||||
self.AngleDamping = num
|
||||
|
||||
if self.phys:IsValid() then
|
||||
self.phys:SetDamping(self.PositionDamping, self.AngleDamping)
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
157
lua/pac3/core/client/parts/player_config.lua
Normal file
157
lua/pac3/core/client/parts/player_config.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "player_config"
|
||||
PART.Group = "entity"
|
||||
PART.Icon = 'icon16/brick.png'
|
||||
|
||||
|
||||
local blood_colors = {
|
||||
dont_bleed = _G.DONT_BLEED,
|
||||
red = _G.BLOOD_COLOR_RED,
|
||||
yellow = _G.BLOOD_COLOR_YELLOW,
|
||||
green = _G.BLOOD_COLOR_GREEN,
|
||||
mech = _G.BLOOD_COLOR_MECH,
|
||||
antlion = _G.BLOOD_COLOR_ANTLION,
|
||||
zombie = _G.BLOOD_COLOR_ZOMBIE,
|
||||
antlion_worker = _G.BLOOD_COLOR_ANTLION_WORKER,
|
||||
}
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:GetSet("MuteSounds", false)
|
||||
BUILDER:GetSet("AllowOggWhenMuted", false)
|
||||
BUILDER:GetSet("HideBullets", false)
|
||||
BUILDER:GetSet("HidePhysgunBeam", false)
|
||||
BUILDER:GetSet("UseLegacyScale", false)
|
||||
BUILDER:GetSet("GrabEarAnimation", true)
|
||||
BUILDER:GetSet("BloodColor", "red", {enums = blood_colors})
|
||||
|
||||
BUILDER:SetPropertyGroup("behavior")
|
||||
BUILDER:GetSet("MuteFootsteps", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("death")
|
||||
BUILDER:GetSet("FallApartOnDeath", false)
|
||||
BUILDER:GetSet("DeathRagdollizeParent", true)
|
||||
BUILDER:GetSet("DrawPlayerOnDeath", false)
|
||||
BUILDER:GetSet("HideRagdollOnDeath", false)
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local ent_fields = {}
|
||||
|
||||
function BUILDER:EntityField(name, field)
|
||||
field = "pac_" .. field
|
||||
|
||||
ent_fields[field] = name
|
||||
|
||||
self.PART["Set" .. name] = function(self, val)
|
||||
self[name] = val
|
||||
|
||||
local owner = self:GetActualOwner()
|
||||
|
||||
if owner:IsValid() then
|
||||
owner[field] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:EntityField("InverseKinematics", "enable_ik")
|
||||
BUILDER:EntityField("MuteFootsteps", "mute_footsteps")
|
||||
BUILDER:EntityField("AnimationRate", "global_animation_rate")
|
||||
BUILDER:EntityField("FallApartOnDeath", "death_physics_parts")
|
||||
BUILDER:EntityField("DeathRagdollizeParent", "death_ragdollize")
|
||||
BUILDER:EntityField("HideRagdollOnDeath", "death_hide_ragdoll")
|
||||
BUILDER:EntityField("DrawPlayerOnDeath", "draw_player_on_death")
|
||||
BUILDER:EntityField("HidePhysgunBeam", "hide_physgun_beam")
|
||||
BUILDER:EntityField("MuteSounds", "mute_sounds")
|
||||
BUILDER:EntityField("AllowOggWhenMuted", "allow_ogg_sounds")
|
||||
BUILDER:EntityField("HideBullets", "hide_bullets")
|
||||
|
||||
function PART:GetActualOwner()
|
||||
local owner = self:GetOwner()
|
||||
if owner:IsValid() and owner:GetRagdollOwner():IsPlayer() then
|
||||
return owner:GetRagdollOwner()
|
||||
end
|
||||
return owner
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local ent = self:GetActualOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if ent:IsPlayer() then
|
||||
return ent:Nick()
|
||||
else
|
||||
return language.GetPhrase(ent:GetClass())
|
||||
end
|
||||
end
|
||||
|
||||
return self.ClassName
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetActualOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "blood_color", ent, blood_colors[self.BloodColor == "" and "red" or self.BloodColor])
|
||||
end
|
||||
|
||||
if ent:IsValid() then
|
||||
for _, field in pairs(ent_fields) do
|
||||
self["Set" .. field](self, self[field])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
local ent = self:GetActualOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.pac_mute_footsteps = self.MuteFootsteps
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetActualOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
local player_owner = self:GetPlayerOwner()
|
||||
|
||||
pac.emut.RestoreMutations(player_owner, "blood_color", ent)
|
||||
|
||||
for key in pairs(ent_fields) do
|
||||
ent[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetBloodColor(str)
|
||||
self.BloodColor = str
|
||||
|
||||
local ent = self:GetActualOwner()
|
||||
if ent:IsValid() then
|
||||
pac.emut.MutateEntity(self:GetPlayerOwner(), "blood_color", ent, blood_colors[self.BloodColor == "" and "red" or self.BloodColor])
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetGrabEarAnimation(b)
|
||||
self.GrabEarAnimation = b
|
||||
|
||||
local ent = self:GetActualOwner()
|
||||
if ent:IsValid() then
|
||||
ent.pac_disable_ear_grab = not b
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
88
lua/pac3/core/client/parts/poseparameter.lua
Normal file
88
lua/pac3/core/client/parts/poseparameter.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "poseparameter"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = {'modifiers', 'entity'}
|
||||
PART.Icon = 'icon16/disconnect.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("PoseParameter", "", {enums = function(part) return part:GetPoseParameterList() end})
|
||||
BUILDER:GetSet("Range", 0)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
return pac.PrettifyName(self:GetPoseParameter())
|
||||
end
|
||||
|
||||
function PART:GetPoseParameterList()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
local out = {}
|
||||
|
||||
if ent:IsValid() then
|
||||
for i = 0, ent:GetNumPoseParameters()-1 do
|
||||
local name = ent:GetPoseParameterName(i)
|
||||
if name ~= "" then
|
||||
out[name] = {name = name, i = i, range = {ent:GetPoseParameterRange(i)}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function PART:SetRange(num)
|
||||
self.Range = num
|
||||
self:UpdateParams()
|
||||
end
|
||||
|
||||
function PART:UpdateParams()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
if not self.pose_params or ent:GetModel() ~= self.last_owner_mdl then
|
||||
self.pose_params = self:GetPoseParameterList()
|
||||
self.last_owner_mdl = ent:GetModel()
|
||||
end
|
||||
|
||||
local data = self.pose_params[self.PoseParameter]
|
||||
|
||||
if data then
|
||||
local num = Lerp((self.Range + 1) / 2, data.range[1] or 0, data.range[2] or 1)
|
||||
|
||||
ent.pac_pose_params = ent.pac_pose_params or {}
|
||||
ent.pac_pose_params[self.UniqueID] = ent.pac_pose_params[self.UniqueID] or {}
|
||||
|
||||
ent.pac_pose_params[self.UniqueID].key = data.name
|
||||
ent.pac_pose_params[self.UniqueID].val = num
|
||||
|
||||
ent:SetPoseParameter(data.name, num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
ent.pac_pose_params = nil
|
||||
ent:ClearPoseParameters()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(ent)
|
||||
self:UpdateParams()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
172
lua/pac3/core/client/parts/projected_texture.lua
Normal file
172
lua/pac3/core/client/parts/projected_texture.lua
Normal file
@@ -0,0 +1,172 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "projected_texture"
|
||||
PART.Group = "effects"
|
||||
PART.Icon = 'icon16/lightbulb.png'
|
||||
PART.ProperColorRange = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Shadows", true)
|
||||
BUILDER:GetSet("Orthographic", false)
|
||||
|
||||
BUILDER:GetSet("NearZ", 1)
|
||||
BUILDER:GetSet("FarZ", 2048)
|
||||
|
||||
BUILDER:GetSet("FOV", 90)
|
||||
BUILDER:GetSet("HorizontalFOV", 90)
|
||||
BUILDER:GetSet("VerticalFOV", 90)
|
||||
|
||||
BUILDER:GetSet("Texture", "effects/flashlight/hard", {editor_panel = "textures"})
|
||||
BUILDER:GetSet("TextureFrame", 0)
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Brightness", 8)
|
||||
BUILDER:GetSet("Color", Vector(1, 1, 1), {editor_panel = "color2"})
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetProjectedTexture()
|
||||
if not self.ptex then
|
||||
self.ptex = ProjectedTexture()
|
||||
end
|
||||
return self.ptex
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local hue = pac.ColorToNames(self:GetColor())
|
||||
return hue .. " projected texture"
|
||||
end
|
||||
|
||||
local vars = {
|
||||
"Shadows",
|
||||
|
||||
"NearZ",
|
||||
"FarZ",
|
||||
|
||||
"FOV",
|
||||
"HorizontalFOV",
|
||||
"VerticalFOV",
|
||||
|
||||
"Orthographic",
|
||||
|
||||
"Texture",
|
||||
"TextureFrame",
|
||||
|
||||
"Brightness",
|
||||
"Color",
|
||||
}
|
||||
|
||||
function PART:OnShow()
|
||||
for _, v in ipairs(vars) do
|
||||
self["Set" .. v](self, self["Get" .. v](self))
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
local ptex = self:GetProjectedTexture()
|
||||
ptex:SetPos(pos)
|
||||
ptex:SetAngles(ang)
|
||||
ptex:Update()
|
||||
end
|
||||
|
||||
|
||||
function PART:SetColor(val)
|
||||
self.Color = val
|
||||
self:GetProjectedTexture():SetColor(Color(val.x*255, val.y*255, val.z*255, 1))
|
||||
end
|
||||
|
||||
function PART:SetBrightness(val)
|
||||
self.Brightness = val
|
||||
self:GetProjectedTexture():SetBrightness(val)
|
||||
end
|
||||
|
||||
function PART:SetOrthographic(val)
|
||||
self.Orthographic = val
|
||||
self:GetProjectedTexture():SetOrthographic(val)
|
||||
end
|
||||
|
||||
function PART:SetVerticalFOV(val)
|
||||
self.VerticalFOV = val
|
||||
self:GetProjectedTexture():SetVerticalFOV(val)
|
||||
end
|
||||
|
||||
function PART:SetHorizontalFOV(val)
|
||||
self.HorizontalFOV = val
|
||||
self:GetProjectedTexture():SetHorizontalFOV(val)
|
||||
end
|
||||
|
||||
|
||||
function PART:SetFOV(val)
|
||||
self.FOV = val
|
||||
self:GetProjectedTexture():SetFOV(val)
|
||||
end
|
||||
|
||||
function PART:SetNearZ(val)
|
||||
self.NearZ = val
|
||||
self:GetProjectedTexture():SetNearZ(val)
|
||||
end
|
||||
|
||||
function PART:SetFarZ(val)
|
||||
self.FarZ = val
|
||||
self:GetProjectedTexture():SetFarZ(val)
|
||||
end
|
||||
|
||||
function PART:SetShadows(val)
|
||||
self.Shadows = val
|
||||
self:GetProjectedTexture():SetEnableShadows(val)
|
||||
end
|
||||
|
||||
function PART:SetTextureFrame(val)
|
||||
self.TextureFrame = val
|
||||
if self.vtf_frame_limit then
|
||||
self:GetProjectedTexture():SetTextureFrame(math.abs(val)%self.vtf_frame_limit)
|
||||
else
|
||||
self:GetProjectedTexture():SetTextureFrame(math.abs(val))
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetTexture(val)
|
||||
if not val then
|
||||
return
|
||||
end
|
||||
|
||||
self.Texture = val
|
||||
|
||||
if not pac.resource.DownloadTexture(val, function(tex, frames)
|
||||
if frames then
|
||||
self.vtf_frame_limit = frames
|
||||
end
|
||||
self:GetProjectedTexture():SetTexture(tex)
|
||||
end, self:GetPlayerOwner()) then
|
||||
self:GetProjectedTexture():SetTexture(val)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
local tex = self:GetProjectedTexture()
|
||||
tex:SetBrightness(0)
|
||||
tex:Update()
|
||||
-- give it one frame to update
|
||||
timer.Simple(0, function()
|
||||
if tex:IsValid() then
|
||||
tex:Remove()
|
||||
end
|
||||
end)
|
||||
self.ptex = nil
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
self:OnHide()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
354
lua/pac3/core/client/parts/projectile.lua
Normal file
354
lua/pac3/core/client/parts/projectile.lua
Normal file
@@ -0,0 +1,354 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
language.Add("pac_projectile", "Projectile")
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.ClassName = "projectile"
|
||||
PART.Group = 'advanced'
|
||||
PART.Icon = 'icon16/bomb.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Speed", 1)
|
||||
BUILDER:GetSet("AddOwnerSpeed", false)
|
||||
BUILDER:GetSet("Damping", 0)
|
||||
BUILDER:GetSet("Gravity", true)
|
||||
BUILDER:GetSet("Collisions", true)
|
||||
BUILDER:GetSet("Sphere", false)
|
||||
BUILDER:GetSet("Radius", 1)
|
||||
BUILDER:GetSet("DamageRadius", 50)
|
||||
BUILDER:GetSet("LifeTime", 5)
|
||||
BUILDER:GetSet("AimDir", false)
|
||||
BUILDER:GetSet("Sticky", false)
|
||||
BUILDER:GetSet("Bounce", 0)
|
||||
BUILDER:GetSet("BulletImpact", false)
|
||||
BUILDER:GetSet("Damage", 0)
|
||||
BUILDER:GetSet("DamageType", "generic", {enums = {
|
||||
generic = 0, --generic damage
|
||||
crush = 1, --caused by physics interaction
|
||||
bullet = 2, --bullet damage
|
||||
slash = 4, --sharp objects, such as manhacks or other npcs attacks
|
||||
burn = 8, --damage from fire
|
||||
vehicle = 16, --hit by a vehicle
|
||||
fall = 32, --fall damage
|
||||
blast = 64, --explosion damage
|
||||
club = 128, --crowbar damage
|
||||
shock = 256, --electrical damage, shows smoke at the damage position
|
||||
sonic = 512, --sonic damage,used by the gargantua and houndeye npcs
|
||||
energybeam = 1024, --laser
|
||||
nevergib = 4096, --don't create gibs
|
||||
alwaysgib = 8192, --always create gibs
|
||||
drown = 16384, --drown damage
|
||||
paralyze = 32768, --same as dmg_poison
|
||||
nervegas = 65536, --neurotoxin damage
|
||||
poison = 131072, --poison damage
|
||||
acid = 1048576, --
|
||||
airboat = 33554432, --airboat gun damage
|
||||
blast_surface = 134217728, --this won't hurt the player underwater
|
||||
buckshot = 536870912, --the pellets fired from a shotgun
|
||||
direct = 268435456, --
|
||||
dissolve = 67108864, --forces the entity to dissolve on death
|
||||
drownrecover = 524288, --damage applied to the player to restore health after drowning
|
||||
physgun = 8388608, --damage done by the gravity gun
|
||||
plasma = 16777216, --
|
||||
prevent_physics_force = 2048, --
|
||||
radiation = 262144, --radiation
|
||||
removenoragdoll = 4194304, --don't create a ragdoll on death
|
||||
slowburn = 2097152, --
|
||||
|
||||
explosion = -1, -- util.BlastDamage
|
||||
fire = -1, -- ent:Ignite(5)
|
||||
|
||||
-- env_entity_dissolver
|
||||
dissolve_energy = 0,
|
||||
dissolve_heavy_electrical = 1,
|
||||
dissolve_light_electrical = 2,
|
||||
dissolve_core_effect = 3,
|
||||
|
||||
heal = -1,
|
||||
armor = -1,
|
||||
}
|
||||
})
|
||||
BUILDER:GetSet("Spread", 0)
|
||||
BUILDER:GetSet("Delay", 0)
|
||||
BUILDER:GetSet("Maximum", 0)
|
||||
BUILDER:GetSet("Mass", 100)
|
||||
BUILDER:GetSet("Attract", 0)
|
||||
BUILDER:GetSet("AttractMode", "projectile_nearest", {enums = {
|
||||
hitpos = "hitpos",
|
||||
hitpos_radius = "hitpos_radius",
|
||||
closest_to_projectile = "closest_to_projectile",
|
||||
closest_to_hitpos = "closest_to_hitpos",
|
||||
}})
|
||||
BUILDER:GetSet("AttractRadius", 200)
|
||||
BUILDER:GetSetPart("OutfitPart")
|
||||
BUILDER:GetSet("Physical", false)
|
||||
BUILDER:GetSet("CollideWithOwner", false)
|
||||
BUILDER:GetSet("CollideWithSelf", false)
|
||||
BUILDER:GetSet("RemoveOnCollide", false)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
PART.Translucent = false
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
-- TODO:
|
||||
-- this makes sure all the parents of this movable have an up-to-date draw position
|
||||
-- GetBonePosition implicitly uses ent:GetPos() as the parent origin which is really bad,
|
||||
-- it should instead be using what pac considers to be the position
|
||||
--self:GetRootPart():CallRecursive("Draw", "opaque")
|
||||
local parents = self:GetParentList()
|
||||
-- call draw from root to the current part only on direct parents to update the position hiearchy
|
||||
for i = #parents, 1, -1 do
|
||||
local part = parents[i]
|
||||
if part.Draw then
|
||||
part:Draw("opaque")
|
||||
end
|
||||
end
|
||||
self:Shoot(self:GetDrawPosition())
|
||||
end
|
||||
end
|
||||
|
||||
function PART:AttachToEntity(ent)
|
||||
if not self.OutfitPart:IsValid() then return false end
|
||||
|
||||
ent.pac_draw_distance = 0
|
||||
|
||||
local tbl = self.OutfitPart:ToTable()
|
||||
|
||||
local group = pac.CreatePart("group", self:GetPlayerOwner())
|
||||
group:SetShowInEditor(false)
|
||||
|
||||
local part = pac.CreatePart(tbl.self.ClassName, self:GetPlayerOwner(), tbl, tostring(tbl))
|
||||
group:AddChild(part)
|
||||
|
||||
group:SetOwner(ent)
|
||||
group.SetOwner = function(s) s.Owner = ent end
|
||||
part:SetHide(false)
|
||||
|
||||
local id = group.Id
|
||||
local owner_id = self:GetPlayerOwnerId()
|
||||
if owner_id then
|
||||
id = id .. owner_id
|
||||
end
|
||||
|
||||
ent:CallOnRemove("pac_projectile_" .. id, function() group:Remove() end)
|
||||
group:CallRecursive("Think")
|
||||
|
||||
ent.RenderOverride = ent.RenderOverride or function()
|
||||
if self.AimDir then
|
||||
ent:SetRenderAngles(ent:GetVelocity():Angle())
|
||||
end
|
||||
end
|
||||
|
||||
ent.pac_projectile_part = group
|
||||
ent.pac_projectile = self
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local enable = CreateClientConVar("pac_sv_projectiles", 0, true)
|
||||
|
||||
function PART:Shoot(pos, ang)
|
||||
local physics = self.Physical
|
||||
|
||||
if physics then
|
||||
if pac.LocalPlayer ~= self:GetPlayerOwner() then return end
|
||||
|
||||
local tbl = {}
|
||||
for key in pairs(self:GetStorableVars()) do
|
||||
tbl[key] = self[key]
|
||||
end
|
||||
|
||||
net.Start("pac_projectile")
|
||||
net.WriteVector(pos)
|
||||
net.WriteAngle(ang)
|
||||
net.WriteTable(tbl)
|
||||
net.SendToServer()
|
||||
else
|
||||
self.projectiles = self.projectiles or {}
|
||||
|
||||
local count = 0
|
||||
|
||||
for key, ent in pairs(self.projectiles) do
|
||||
if not ent:IsValid() then
|
||||
self.projectiles[key] = nil
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
local max = math.min(self.Maximum, 100)
|
||||
|
||||
if max == 0 then
|
||||
max = 100
|
||||
end
|
||||
|
||||
if count > max then
|
||||
return
|
||||
end
|
||||
|
||||
local function spawn()
|
||||
|
||||
if not self:IsValid() then return end
|
||||
|
||||
local ent = pac.CreateEntity("models/props_junk/popcan01a.mdl")
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
local idx = table.insert(self.projectiles, ent)
|
||||
|
||||
ent:AddCallback("PhysicsCollide", function(ent, data)
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if self.Bounce > 0 then
|
||||
timer.Simple(0, function()
|
||||
if phys:IsValid() then
|
||||
phys:SetVelocity(data.OurOldVelocity - 2 * (data.HitNormal:Dot(data.OurOldVelocity) * data.HitNormal) * self.Bounce)
|
||||
end
|
||||
end)
|
||||
elseif self.Sticky then
|
||||
phys:SetVelocity(Vector(0,0,0))
|
||||
phys:EnableMotion(false)
|
||||
ent.pac_stuck = data.OurOldVelocity
|
||||
end
|
||||
|
||||
if self.BulletImpact then
|
||||
ent:FireBullets{
|
||||
Attacker = ent:GetOwner(),
|
||||
Damage = 0,
|
||||
Force = 0,
|
||||
Num = 1,
|
||||
Src = data.HitPos - data.HitNormal,
|
||||
Dir = data.HitNormal,
|
||||
Distance = 10,
|
||||
}
|
||||
end
|
||||
|
||||
if self.RemoveOnCollide then
|
||||
timer.Simple(0.01, function() SafeRemoveEntity(ent) end)
|
||||
end
|
||||
end)
|
||||
|
||||
ent:SetOwner(self:GetPlayerOwner(true))
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
|
||||
|
||||
if self.Sphere then
|
||||
ent:PhysicsInitSphere(math.Clamp(self.Radius, 1, 30))
|
||||
else
|
||||
ent:PhysicsInitBox(Vector(1,1,1) * - math.Clamp(self.Radius, 1, 30), Vector(1,1,1) * math.Clamp(self.Radius, 1, 30))
|
||||
end
|
||||
|
||||
ent.RenderOverride = function()
|
||||
if not self:IsValid() then
|
||||
return
|
||||
end
|
||||
|
||||
if not self:GetRootPart():GetOwner():IsValid() then
|
||||
timer.Simple(0, function() SafeRemoveEntity(ent) end)
|
||||
end
|
||||
|
||||
if self.AimDir then
|
||||
if ent.pac_stuck then
|
||||
ent:SetRenderAngles(ent.pac_stuck:Angle())
|
||||
else
|
||||
local angle = ent:GetVelocity():Angle()
|
||||
ent:SetRenderAngles(angle)
|
||||
ent.last_angle = angle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
phys:EnableGravity(self.Gravity)
|
||||
phys:AddVelocity((ang:Forward() + (VectorRand():Angle():Forward() * self.Spread)) * self.Speed * 1000)
|
||||
if self.AddOwnerSpeed and ent:GetOwner():IsValid() then
|
||||
phys:AddVelocity(ent:GetOwner():GetVelocity())
|
||||
end
|
||||
phys:EnableCollisions(self.Collisions)
|
||||
phys:SetDamping(self.Damping, 0)
|
||||
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
|
||||
|
||||
if self:AttachToEntity(ent) then
|
||||
timer.Simple(math.Clamp(self.LifeTime, 0, 10), function()
|
||||
if ent:IsValid() then
|
||||
if ent.pac_projectile_part and ent.pac_projectile_part:IsValid() then
|
||||
ent.pac_projectile_part:Remove()
|
||||
end
|
||||
|
||||
timer.Simple(0.5, function()
|
||||
SafeRemoveEntity(ent)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if self.Delay == 0 then
|
||||
spawn()
|
||||
else
|
||||
timer.Simple(self.Delay, spawn)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
if not self.Physical and self.projectiles then
|
||||
for key, ent in pairs(self.projectiles) do
|
||||
SafeRemoveEntity(ent)
|
||||
end
|
||||
|
||||
self.projectiles = {}
|
||||
end
|
||||
end
|
||||
--[[
|
||||
function PART:OnHide()
|
||||
if self.RemoveOnHide then
|
||||
self:OnRemove()
|
||||
end
|
||||
end
|
||||
]]
|
||||
do -- physical
|
||||
local Entity = Entity
|
||||
local projectiles = {}
|
||||
pac.AddHook("Think", "pac_projectile", function()
|
||||
for key, data in pairs(projectiles) do
|
||||
if not data.ply:IsValid() then
|
||||
projectiles[key] = nil
|
||||
goto CONTINUE
|
||||
end
|
||||
|
||||
local ent = Entity(data.ent_id)
|
||||
|
||||
if ent:IsValid() and ent:GetClass() == "pac_projectile" then
|
||||
local part = pac.GetPartFromUniqueID(pac.Hash(data.ply), data.partuid)
|
||||
if part:IsValid() and part:GetPlayerOwner() == data.ply then
|
||||
part:AttachToEntity(ent)
|
||||
end
|
||||
projectiles[key] = nil
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("pac_projectile_attach", function()
|
||||
local ply = net.ReadEntity()
|
||||
local ent_id = net.ReadInt(16)
|
||||
local partuid = net.ReadString()
|
||||
|
||||
if ply:IsValid() then
|
||||
table.insert(projectiles, {ply = ply, ent_id = ent_id, partuid = partuid})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
1234
lua/pac3/core/client/parts/proxy.lua
Normal file
1234
lua/pac3/core/client/parts/proxy.lua
Normal file
File diff suppressed because it is too large
Load Diff
420
lua/pac3/core/client/parts/script.lua
Normal file
420
lua/pac3/core/client/parts/script.lua
Normal file
@@ -0,0 +1,420 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "script"
|
||||
|
||||
PART.ThinkTime = 0
|
||||
PART.Group = 'advanced'
|
||||
PART.Icon = 'icon16/page_white_gear.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Code", "")
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
local blacklist = {
|
||||
"do",
|
||||
"end",
|
||||
"function",
|
||||
"repeat",
|
||||
"while",
|
||||
}
|
||||
|
||||
local lib =
|
||||
{
|
||||
math = {
|
||||
pi = math.pi,
|
||||
random = math.random,
|
||||
abs = math.abs,
|
||||
acos = math.acos,
|
||||
asin = math.asin,
|
||||
atan = math.atan,
|
||||
atan2 = math.atan2,
|
||||
ceil = math.ceil,
|
||||
cos = math.cos,
|
||||
cosh = math.cosh,
|
||||
deg = math.deg,
|
||||
exp = math.exp,
|
||||
floor = math.floor,
|
||||
frexp = math.frexp,
|
||||
ldexp = math.ldexp,
|
||||
log = math.log,
|
||||
log10 = math.log10,
|
||||
max = math.max,
|
||||
min = math.min,
|
||||
rad = math.rad,
|
||||
sin = math.sin,
|
||||
sinh = math.sinh,
|
||||
sqrt = math.sqrt,
|
||||
tanh = math.tanh,
|
||||
tan = math.tan,
|
||||
|
||||
clamp = math.Clamp,
|
||||
randomx = math.Rand,
|
||||
},
|
||||
|
||||
string = {
|
||||
find = string.find,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
local function translate_xyz(x, y, z, T, def)
|
||||
if T == "Vector" then
|
||||
|
||||
def.x = x or def.x
|
||||
def.y = y or def.y
|
||||
def.z = z or def.z
|
||||
|
||||
return def
|
||||
elseif T == "Angle" then
|
||||
|
||||
def.p = x or def.p
|
||||
def.y = y or def.y
|
||||
def.r = z or def.r
|
||||
|
||||
return def
|
||||
elseif T == "number" then
|
||||
return tonumber(x) or def -- inf protection here
|
||||
elseif T == "string" then
|
||||
return tostring(x)
|
||||
end
|
||||
end
|
||||
|
||||
local function translate_value(val, T)
|
||||
if T == "Vector" then
|
||||
return val.x, val.y, val.z
|
||||
elseif T == "Angle" then
|
||||
return val.p, val.y, val.r
|
||||
elseif T == "number" or T == "string" then
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateDummies(parts)
|
||||
|
||||
local obj = {
|
||||
SetProperty = function(_, key, x, y, z)
|
||||
if not key then return end
|
||||
|
||||
for _, v in pairs(parts) do
|
||||
if v:IsValid() and v.StorableVars[key] then
|
||||
local def = v[key]
|
||||
local val = translate_xyz(x ,y, z, type(def), def)
|
||||
|
||||
v["Set" .. key](v, val)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
EventHide = function(_, b)
|
||||
for _, v in pairs(parts) do
|
||||
if v:IsValid() then
|
||||
v:SetEventTrigger(self, not not b)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
EventShow = function(_, b)
|
||||
for _, v in pairs(parts) do
|
||||
if v:IsValid() then
|
||||
v:SetEventTrigger(self, not b)
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
local function CreateDummy(part, store, self)
|
||||
if not part or not part:IsValid() then return end
|
||||
if part.dummy_part then return part.dummy_part end
|
||||
|
||||
store.parts[part.UniqueID] = {}
|
||||
|
||||
local META =
|
||||
{
|
||||
SetProperty = function(_, key, x, y, z)
|
||||
if key and part.StorableVars[key] then
|
||||
local def = part[key]
|
||||
local val = translate_xyz(x ,y, z, type(def), def)
|
||||
|
||||
part["Set" .. key](part, val)
|
||||
end
|
||||
end,
|
||||
|
||||
GetProperty = function(_, key)
|
||||
if key and part.StorableVars[key] then
|
||||
local val = part["Get" .. key](part)
|
||||
|
||||
if val then
|
||||
local x, y, z = translate_value(val, type(val))
|
||||
|
||||
if x then
|
||||
return x, y, z
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
EventHide = function(_, b)
|
||||
part:SetEventTrigger(self, not not b)
|
||||
end,
|
||||
|
||||
EventShow = function(_, b)
|
||||
part:SetEventTrigger(self, not b)
|
||||
end,
|
||||
|
||||
GetChildren = function()
|
||||
return CreateDummies(part:GetChildren(), self)
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
local obj = setmetatable(
|
||||
{},
|
||||
{
|
||||
__index = function(_, key)
|
||||
if not part:IsValid() then return end
|
||||
|
||||
if store.parts[part.UniqueID][key] then
|
||||
return store.parts[part.UniqueID][key]
|
||||
end
|
||||
|
||||
return META[key]
|
||||
end,
|
||||
|
||||
__newindex = function(_, key, val)
|
||||
if not part:IsValid() then return end
|
||||
|
||||
store.parts[part.UniqueID][key] = val
|
||||
end,
|
||||
}
|
||||
)
|
||||
|
||||
part.dummy_part = obj
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
local function get_entity(part)
|
||||
local ent = part:GetRootPart():GetOwner()
|
||||
return ent == pac.LocalPlayer:GetViewModel() and pac.LocalPlayer or ent
|
||||
end
|
||||
|
||||
function PART:CompileCode()
|
||||
local code = self.Code
|
||||
|
||||
for _, word in pairs(blacklist) do
|
||||
if code:find("[%p%s]" .. word) or code:find(word .. "[%p%s]") then
|
||||
return false, string.format("illegal characters used %q", word)
|
||||
end
|
||||
end
|
||||
|
||||
local func = CompileString(code, "SCRIPT_ENV", false)
|
||||
|
||||
if isstring(func) then
|
||||
return false, func
|
||||
end
|
||||
|
||||
local store = {globals = {}, parts = {}}
|
||||
|
||||
local extra_lib =
|
||||
{
|
||||
print = function(...)
|
||||
if self:GetPlayerOwner() == pac.LocalPlayer then
|
||||
print(...)
|
||||
|
||||
local str = ""
|
||||
local count = select("#", ...)
|
||||
|
||||
for i = 1, count do
|
||||
str = str .. tostring(select(i, ...))
|
||||
if i ~= count then
|
||||
str = str .. ", "
|
||||
end
|
||||
end
|
||||
|
||||
self.script_printing = str
|
||||
end
|
||||
end,
|
||||
|
||||
owner = {
|
||||
GetFOV = function()
|
||||
local ent = get_entity(self)
|
||||
|
||||
if ent:IsValid() then
|
||||
return ent:GetFOV()
|
||||
end
|
||||
end,
|
||||
|
||||
GetHealth = function()
|
||||
local ent = get_entity(self)
|
||||
|
||||
if ent:IsValid() then
|
||||
return ent:Health()
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
parts = {
|
||||
GetParent = function(level)
|
||||
level = level or 1
|
||||
local parent = self
|
||||
|
||||
for _ = 1, math.Clamp(level, 1, 30) do
|
||||
parent = parent:GetParent()
|
||||
end
|
||||
|
||||
return CreateDummy(parent, store, self)
|
||||
end,
|
||||
|
||||
FindMultiple = function(str)
|
||||
local parts = {}
|
||||
|
||||
for _, part in pairs(pac.GetParts()) do
|
||||
if
|
||||
part:GetPlayerOwner() == self:GetPlayerOwner() and
|
||||
pac.StringFind(part:GetName(), str)
|
||||
then
|
||||
table.insert(parts, part)
|
||||
end
|
||||
end
|
||||
|
||||
return CreateDummies(parts, self)
|
||||
end,
|
||||
|
||||
FindMultipleWithProperty = function()
|
||||
local parts = {}
|
||||
|
||||
for key, part in pairs(pac.GetParts()) do
|
||||
if
|
||||
part:GetPlayerOwner() == self:GetPlayerOwner() and
|
||||
part.StorableVars[key] and
|
||||
part["Get" .. key] and part["Get" .. key]()
|
||||
then
|
||||
table.insert(parts, part)
|
||||
end
|
||||
end
|
||||
|
||||
return CreateDummies(parts, self)
|
||||
end,
|
||||
|
||||
Find = function(str)
|
||||
for _, part in pairs(pac.GetParts()) do
|
||||
if
|
||||
part:GetPlayerOwner() == self:GetPlayerOwner() and
|
||||
(part.UniqueID == str or part:GetName() == str)
|
||||
then
|
||||
return CreateDummy(part, store, self)
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
local env = {}
|
||||
|
||||
env.__index = function(_, key)
|
||||
if key == "this" or key == "self" then
|
||||
return CreateDummy(self, store, self)
|
||||
end
|
||||
|
||||
if key == "T" or key == "TIME" then
|
||||
return RealTime()
|
||||
end
|
||||
|
||||
if key == "CT" or key == "CURTIME" then
|
||||
return CurTime()
|
||||
end
|
||||
|
||||
if lib[key] then
|
||||
return lib[key]
|
||||
end
|
||||
|
||||
if extra_lib[key] then
|
||||
return extra_lib[key]
|
||||
end
|
||||
|
||||
if store[key] then
|
||||
return store[key]
|
||||
end
|
||||
end
|
||||
|
||||
env.__newindex = function(_, key, val)
|
||||
store[key] = val
|
||||
end
|
||||
|
||||
self.valid_functions = {
|
||||
SetProperty = "m",
|
||||
GetProperty = "m",
|
||||
GetChildren = "m",
|
||||
EventHide = "m",
|
||||
EventShow = "m",
|
||||
self = "e",
|
||||
this = "e",
|
||||
T = "e",
|
||||
TIME = "e",
|
||||
CT = "e",
|
||||
CURTIME = "e"
|
||||
}
|
||||
|
||||
local function scan(tbl)
|
||||
for key, val in pairs(tbl) do
|
||||
self.valid_functions[key] = val
|
||||
|
||||
if istable(val) then
|
||||
scan(val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scan(lib)
|
||||
scan(extra_lib)
|
||||
|
||||
setfenv(func, setmetatable({}, env))
|
||||
|
||||
return true, func
|
||||
end
|
||||
|
||||
function PART:ShouldHighlight(str)
|
||||
return self.valid_functions and self.valid_functions[str]
|
||||
end
|
||||
|
||||
function PART:SetCode(code)
|
||||
self.Code = code
|
||||
local ok, func = self:CompileCode()
|
||||
|
||||
if ok then
|
||||
self.func = func
|
||||
self:SetError()
|
||||
else
|
||||
self:SetError(func)
|
||||
self.func = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
if self.func then
|
||||
local ok, err = pcall(self.func)
|
||||
if not ok then
|
||||
self:SetError(err)
|
||||
self.func = nil
|
||||
else
|
||||
self:SetError()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("pac_register_script_part", function()
|
||||
BUILDER:Register()
|
||||
end)
|
||||
43
lua/pac3/core/client/parts/shake.lua
Normal file
43
lua/pac3/core/client/parts/shake.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.ClassName = "shake"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/transmit.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:SetPropertyGroup("shake")
|
||||
BUILDER:GetSet("Amplitude", 1)
|
||||
BUILDER:GetSet("Falloff", false)
|
||||
BUILDER:GetSet("Frequency", 1)
|
||||
BUILDER:GetSet("Duration", 0.5)
|
||||
BUILDER:GetSet("Radius", 100)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
local position = self:GetDrawPosition()
|
||||
local eyedistance = position:Distance(pac.EyePos)
|
||||
local radius = math.Clamp(self.Radius, 0.0001, 500)
|
||||
|
||||
if eyedistance < radius then
|
||||
local amplitude = self.Amplitude
|
||||
if self.Falloff then
|
||||
amplitude = amplitude * (1 - (eyedistance / radius))
|
||||
end
|
||||
util.ScreenShake(position, amplitude, self.Frequency, math.Clamp(self.Duration, 0.0001, 2), 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
346
lua/pac3/core/client/parts/sound.lua
Normal file
346
lua/pac3/core/client/parts/sound.lua
Normal file
@@ -0,0 +1,346 @@
|
||||
--[[
|
||||
| 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 webaudio = include("pac3/libraries/webaudio.lua")
|
||||
pac.webaudio2 = webaudio
|
||||
local BUILDER, PART = pac.PartTemplate("base_movable")
|
||||
|
||||
PART.FriendlyName = "web sound"
|
||||
PART.ClassName = "sound2"
|
||||
|
||||
PART.Icon = 'icon16/music.png'
|
||||
PART.Group = 'effects'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:GetSet("Path", "", {editor_panel = "sound"})
|
||||
BUILDER:GetSet("Volume", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Pitch", 1, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("Radius", 1500)
|
||||
BUILDER:GetSet("Doppler", false)
|
||||
BUILDER:GetSet("MinPitch", 0, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("MaxPitch", 0, {editor_sensitivity = 0.125})
|
||||
|
||||
BUILDER:SetPropertyGroup("playback")
|
||||
BUILDER:GetSet("PlayCount", 1,
|
||||
{editor_onchange =
|
||||
function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Round(math.max(num, 0))
|
||||
end, editor_friendly = "PlayCount (0=loop)"}
|
||||
)
|
||||
BUILDER:GetSet("Sequential",false,{description = "if there are multiple sounds (separated by ; ), plays these sounds in sequential order instead of randomly"})
|
||||
BUILDER:GetSet("SequentialStep", 1,
|
||||
{editor_onchange =
|
||||
function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Round(num)
|
||||
end})
|
||||
BUILDER:GetSet("StopOnHide", false)
|
||||
BUILDER:GetSet("PauseOnHide", false)
|
||||
BUILDER:GetSet("Overlapping", false)
|
||||
BUILDER:GetSet("PlayOnFootstep", false)
|
||||
|
||||
BUILDER:SetPropertyGroup("filter")
|
||||
BUILDER:GetSet("FilterType", 0, {enums = {
|
||||
none = "0",
|
||||
lowpass = "1",
|
||||
highpass = "2",
|
||||
}})
|
||||
BUILDER:GetSet("FilterFraction", 1, {editor_sensitivity = 0.125, editor_clamp = {0, 1}})
|
||||
|
||||
BUILDER:SetPropertyGroup("echo")
|
||||
BUILDER:GetSet("Echo", false)
|
||||
BUILDER:GetSet("EchoDelay", 0.5, {editor_sensitivity = 0.125})
|
||||
BUILDER:GetSet("EchoFeedback", 0.75, {editor_sensitivity = 0.125})
|
||||
|
||||
BUILDER:SetPropertyGroup("lfo")
|
||||
BUILDER:GetSet("PitchLFOAmount", 0, {editor_sensitivity = 0.125, editor_friendly = "pitch amount"})
|
||||
BUILDER:GetSet("PitchLFOTime", 0, {editor_sensitivity = 0.125, editor_friendly = "pitch time"})
|
||||
|
||||
BUILDER:GetSet("VolumeLFOAmount", 0, {editor_sensitivity = 0.125, editor_friendly = "volume amount"})
|
||||
BUILDER:GetSet("VolumeLFOTime", 0, {editor_sensitivity = 0.125, editor_friendly = "volume time"})
|
||||
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:Initialize()
|
||||
webaudio.Initialize()
|
||||
self.streams = {}
|
||||
end
|
||||
|
||||
function PART:GetNiceName()
|
||||
local path = self:GetPath() .. ";"
|
||||
local tbl = {}
|
||||
for i, path in ipairs(path:Split(";")) do
|
||||
if path ~= "" then
|
||||
if path:StartWith("http") then
|
||||
path = path:gsub("%%(..)", function(char)
|
||||
local num = tonumber("0x" .. char)
|
||||
if num then
|
||||
return string.char(num)
|
||||
end
|
||||
end)
|
||||
end
|
||||
tbl[i] = pac.PrettifyName(("/".. path):match(".+/(.-)%.") or path:match("(.-)%.")) or "sound"
|
||||
end
|
||||
end
|
||||
return table.concat(tbl, ";")
|
||||
end
|
||||
|
||||
local stream_vars = {}
|
||||
|
||||
local BIND = function(propertyName, setterMethodName, check)
|
||||
table.insert(stream_vars, propertyName)
|
||||
setterMethodName = setterMethodName or "Set" .. propertyName
|
||||
PART["Set" .. propertyName] = function(self, value)
|
||||
if check then
|
||||
value = check(value)
|
||||
end
|
||||
|
||||
for url, stream in pairs(self.streams) do
|
||||
if stream:IsValid() then
|
||||
stream[setterMethodName](stream, value)
|
||||
else
|
||||
self.streams[url] = nil
|
||||
end
|
||||
end
|
||||
|
||||
self[propertyName] = value
|
||||
end
|
||||
end
|
||||
|
||||
BIND("Pitch", "SetPlaybackRate")
|
||||
BIND("PlayCount", "SetMaxLoopCount" )
|
||||
BIND("Volume", nil, function(n) return math.Clamp(n, 0, 4) end)
|
||||
BIND("Radius", "SetSourceRadius" )
|
||||
|
||||
BIND("FilterType")
|
||||
BIND("FilterFraction")
|
||||
|
||||
BIND("Echo")
|
||||
BIND("EchoDelay")
|
||||
BIND("EchoFeedback", nil, function(n) return math.Clamp(n, 0, 0.99) end)
|
||||
|
||||
BIND("PitchLFOAmount")
|
||||
BIND("PitchLFOTime")
|
||||
|
||||
BIND("VolumeLFOAmount")
|
||||
BIND("VolumeLFOTime")
|
||||
|
||||
BIND("Doppler")
|
||||
|
||||
function PART:OnThink()
|
||||
local owner = self:GetRootPart():GetOwner()
|
||||
|
||||
for url, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[url] = nil goto CONTINUE end
|
||||
|
||||
if self.PlayCount == 0 then
|
||||
stream:Resume()
|
||||
end
|
||||
|
||||
if stream.owner_set ~= owner and owner:IsValid() then
|
||||
stream:SetSourceEntity(owner, true)
|
||||
stream.owner_set = owner
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
|
||||
if self.last_playonfootstep ~= self.PlayOnFootstep then
|
||||
local ent = self:GetOwner()
|
||||
if ent:IsValid() and ent:IsPlayer() then
|
||||
ent.pac_footstep_override = ent.pac_footstep_override or {}
|
||||
|
||||
if self.PlayOnFootstep then
|
||||
ent.pac_footstep_override[self.UniqueID] = self
|
||||
else
|
||||
ent.pac_footstep_override[self.UniqueID] = nil
|
||||
end
|
||||
|
||||
if table.Count(ent.pac_footstep_override) == 0 then
|
||||
ent.pac_footstep_override = nil
|
||||
end
|
||||
|
||||
self.last_playonfootstep = self.PlayOnFootstep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetPath(path)
|
||||
self.seq_index = 1
|
||||
self.Path = path
|
||||
|
||||
local paths = {}
|
||||
|
||||
for _, path in ipairs(path:Split(";")) do
|
||||
local min, max = path:match(".+%[(.-),(.-)%]")
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
|
||||
if min and max then
|
||||
for i = min, max do
|
||||
table.insert(paths, (path:gsub("%[.-%]", i)))
|
||||
end
|
||||
else
|
||||
table.insert(paths, path)
|
||||
end
|
||||
end
|
||||
|
||||
for _, stream in pairs(self.streams) do
|
||||
if stream:IsValid() then
|
||||
stream:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
self.streams = {}
|
||||
|
||||
local function load(path)
|
||||
local stream = webaudio.CreateStream(path)
|
||||
self.streams[path] = stream
|
||||
|
||||
stream:Set3D(true)
|
||||
stream.OnLoad = function()
|
||||
for _, key in ipairs(stream_vars) do
|
||||
self["Set" .. key](self, self["Get" .. key](self))
|
||||
end
|
||||
end
|
||||
stream.OnError = function(_, err, info)
|
||||
info = info or "unknown error"
|
||||
if self:IsValid() and pac.LocalPlayer == self:GetPlayerOwner() and pace and pace.IsActive() then
|
||||
if pace and pace.current_part == self and not IsValid(pace.BusyWithProperties) then
|
||||
pace.MessagePrompt(err .. "\n" .. info, "OGG error for" .. path, "OK")
|
||||
else
|
||||
pac.Message("OGG error: ", err, " reason: ", err .. "\n" .. info, "OGG error for" .. path)
|
||||
self:SetError("OGG error: " .. err .. "\n" .. info .. "\nfor:" .. path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
stream.UpdateSourcePosition = function()
|
||||
if self:IsValid() then
|
||||
stream.SourcePosition = self:GetDrawPosition()
|
||||
end
|
||||
end
|
||||
|
||||
if
|
||||
pace and
|
||||
pace.Editor:IsValid() and
|
||||
pace.current_part:IsValid() and
|
||||
pace.current_part.ClassName == "ogg2" and
|
||||
self:GetPlayerOwner() == pac.LocalPlayer
|
||||
then
|
||||
stream:Play()
|
||||
end
|
||||
end
|
||||
|
||||
for _, path in ipairs(paths) do
|
||||
local info = sound.GetProperties(path)
|
||||
if info then
|
||||
path = info.sound
|
||||
end
|
||||
|
||||
if not string.StartsWith(path, "http") or not pac.resource.Download(path, function(path) load("data/" .. path) end)
|
||||
|
||||
then load("sound/" .. path)
|
||||
end
|
||||
end
|
||||
self.paths = paths
|
||||
end
|
||||
|
||||
PART.last_stream = NULL
|
||||
|
||||
function PART:PlaySound(_, additiveVolumeFraction)
|
||||
--PrintTable(self.streams)
|
||||
additiveVolumeFraction = additiveVolumeFraction or 0
|
||||
|
||||
local stream = table.Random(self.streams) or NULL
|
||||
if not stream:IsValid() then return end
|
||||
|
||||
if self.Sequential then
|
||||
|
||||
self.seq_index = self.seq_index or 1
|
||||
|
||||
local basepath = self.paths[self.seq_index] or self.paths[1]
|
||||
local snd = "sound/".. basepath
|
||||
|
||||
local cached_path = "data/pac3_cache/downloads/" .. pac.Hash(basepath) .. ".dat"
|
||||
|
||||
if string.find(basepath, "^http") then
|
||||
snd = cached_path
|
||||
end
|
||||
|
||||
if self.streams[snd]:IsValid() then
|
||||
stream = self.streams[snd]
|
||||
print(snd,self.seq_index)
|
||||
end
|
||||
self.seq_index = self.seq_index + self.SequentialStep
|
||||
if self.seq_index > #self.paths then
|
||||
self.seq_index = self.seq_index - #self.paths
|
||||
elseif self.seq_index < 1 then
|
||||
self.seq_index = self.seq_index + #self.paths
|
||||
end
|
||||
end
|
||||
|
||||
stream:SetAdditiveVolumeModifier(additiveVolumeFraction)
|
||||
|
||||
if self.last_stream:IsValid() and not self.Overlapping and not self.PauseOnHide then
|
||||
self.last_stream:Stop()
|
||||
end
|
||||
|
||||
if self.MinPitch ~= self.MaxPitch then
|
||||
stream:SetAdditivePitchModifier(math.Rand(self.MinPitch, self.MaxPitch))
|
||||
else
|
||||
stream:SetAdditivePitchModifier(0)
|
||||
end
|
||||
|
||||
if self.PauseOnHide then
|
||||
stream:Resume()
|
||||
else
|
||||
stream:Play()
|
||||
end
|
||||
|
||||
self.last_stream = stream
|
||||
end
|
||||
|
||||
function PART:StopSound()
|
||||
for key, stream in pairs(self.streams) do
|
||||
if stream:IsValid() then
|
||||
if self.PauseOnHide then
|
||||
stream:Pause()
|
||||
elseif self.StopOnHide then
|
||||
stream:Stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if not from_rendering then
|
||||
self:PlaySound()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self:StopSound()
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
for key, stream in pairs(self.streams) do
|
||||
if not stream:IsValid() then self.streams[key] = nil goto CONTINUE end
|
||||
|
||||
stream:Remove()
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
145
lua/pac3/core/client/parts/sprite.lua
Normal file
145
lua/pac3/core/client/parts/sprite.lua
Normal file
@@ -0,0 +1,145 @@
|
||||
--[[
|
||||
| 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 render_SetMaterial = render.SetMaterial
|
||||
local render_DrawSprite = render.DrawSprite
|
||||
local Color = Color
|
||||
local Vector = Vector
|
||||
local cam_IgnoreZ = cam.IgnoreZ
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "sprite"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/layers.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:SetPropertyGroup("generic")
|
||||
BUILDER:GetSet("IgnoreZ", false)
|
||||
BUILDER:GetSet("SizeX", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("SizeY", 1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("SpritePath", "sprites/grip", {editor_panel = "material"})
|
||||
|
||||
BUILDER:SetPropertyGroup("orientation")
|
||||
BUILDER:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
|
||||
BUILDER:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
BUILDER:GetSet("Translucent", true)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
if not self:GetSpritePath() then
|
||||
return "error"
|
||||
end
|
||||
|
||||
local match = pac.PrettifyName("/" .. self:GetSpritePath()):match(".+/(.+)")
|
||||
return match and match:gsub("%..+", "") or "error"
|
||||
end
|
||||
|
||||
function PART:SetColor(v)
|
||||
self.ColorC = self.ColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.ColorC.r = v.r
|
||||
self.ColorC.g = v.g
|
||||
self.ColorC.b = v.b
|
||||
|
||||
self.Color = v
|
||||
end
|
||||
|
||||
function PART:SetAlpha(n)
|
||||
self.ColorC = self.ColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.ColorC.a = n * 255
|
||||
|
||||
self.Alpha = n
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetSpritePath(self.SpritePath)
|
||||
end
|
||||
|
||||
function PART:SetSpritePath(var)
|
||||
self:SetMaterial(var)
|
||||
end
|
||||
|
||||
function PART:FixMaterial()
|
||||
local mat = self.Materialm
|
||||
|
||||
if not mat then return end
|
||||
|
||||
local shader = mat:GetShader()
|
||||
|
||||
if shader == "VertexLitGeneric" or shader == "Cable" then
|
||||
local tex_path = mat:GetTexture("$basetexture")
|
||||
|
||||
if tex_path then
|
||||
local params = {}
|
||||
|
||||
params["$basetexture"] = tex_path:GetName()
|
||||
params["$vertexcolor"] = 1
|
||||
params["$vertexalpha"] = 1
|
||||
|
||||
self.Materialm = CreateMaterial("pac_fixmat_" .. os.clock(), "UnlitGeneric", params)
|
||||
self.Materialm:SetTexture("$basetexture", tex_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var, nil, "UnlitGeneric", {["$translucent"] = "1"}) then
|
||||
if isstring(var) then
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
elseif type(var) == "IMaterial" then
|
||||
self.Materialm = var
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
self:FixMaterial()
|
||||
|
||||
self.SpritePath = var
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local mat = self.MaterialOverride or self.Materialm
|
||||
if mat then
|
||||
if self.IgnoreZ then
|
||||
cam_IgnoreZ(true)
|
||||
end
|
||||
|
||||
local old_alpha
|
||||
if pac.drawing_motionblur_alpha then
|
||||
if not self.ColorC then self:SetColor(self:GetColor()) end
|
||||
old_alpha = self.ColorC.a
|
||||
self.ColorC.a = pac.drawing_motionblur_alpha*255
|
||||
--print(self.ColorC, pac.drawing_motionblur_alpha*255)
|
||||
end
|
||||
|
||||
local pos = self:GetDrawPosition()
|
||||
|
||||
render_SetMaterial(mat)
|
||||
render_DrawSprite(pos, self.SizeX * self.Size, self.SizeY * self.Size, self.ColorC)
|
||||
|
||||
if self.IgnoreZ then
|
||||
cam_IgnoreZ(false)
|
||||
end
|
||||
|
||||
if pac.drawing_motionblur_alpha then
|
||||
self.ColorC.a = old_alpha
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
181
lua/pac3/core/client/parts/submaterial.lua
Normal file
181
lua/pac3/core/client/parts/submaterial.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
--[[
|
||||
| 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 BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "submaterial"
|
||||
|
||||
PART.Icon = 'icon16/picture_edit.png'
|
||||
PART.Group = {'model', 'entity'}
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Material", "")
|
||||
BUILDER:GetSet("SubMaterialId", 1, {
|
||||
editor_onchange = function(self, num)
|
||||
num = tonumber(num) or 0
|
||||
local maxnum = 16
|
||||
|
||||
return math.floor(math.Clamp(num, 0, maxnum))
|
||||
end,
|
||||
|
||||
enums = function(part)
|
||||
local tbl = {}
|
||||
for i,v in ipairs(part:GetSubMaterialIdList()) do
|
||||
tbl[v] = tostring(i)
|
||||
end
|
||||
return tbl
|
||||
end,
|
||||
})
|
||||
BUILDER:GetSet("RootOwner", false, { hide_in_editor = true })
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:SetRootOwner(b)
|
||||
self:SetRootOwnerDeprecated(b)
|
||||
end
|
||||
|
||||
function PART:GetSubMaterialIdList()
|
||||
local out = {}
|
||||
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() and ent.GetMaterials and #ent:GetMaterials() > 0 then
|
||||
out = ent:GetMaterials()
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function PART:UpdateSubMaterialId(id, material)
|
||||
id = tonumber(id) or self.SubMaterialId
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent ~= self.sub_last_owner then
|
||||
if IsValid(self.sub_last_owner) then
|
||||
self.sub_last_owner:SetSubMaterial(self.sub_last_owner_sub_id - 1, "")
|
||||
|
||||
if self.sub_last_owner.pac_submaterials then
|
||||
self.sub_last_owner.pac_submaterials[self.sub_last_owner_sub_id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
self.sub_last_owner = ent
|
||||
self.sub_last_owner_sub_id = id
|
||||
end
|
||||
|
||||
if not ent:IsValid() or not ent.GetMaterials then return end
|
||||
ent.pac_submaterials = ent.pac_submaterials or {}
|
||||
|
||||
local mat = self.Materialm
|
||||
|
||||
if not material then
|
||||
if self.Material and self.Material ~= "" and mat and not mat:IsError() then
|
||||
local matName = mat:GetName()
|
||||
material = matName:find("/", 1, true) and matName or "!" .. matName
|
||||
else
|
||||
material = ''
|
||||
end
|
||||
end
|
||||
|
||||
if id > 0 then
|
||||
ent.pac_submaterials[id] = material
|
||||
ent:SetSubMaterial(id - 1, material)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:PostApplyFixes()
|
||||
self:UpdateSubMaterialId()
|
||||
end
|
||||
|
||||
function PART:SetSubMaterialId(num)
|
||||
self:UpdateSubMaterialId(self.SubMaterialId, "")
|
||||
self.SubMaterialId = tonumber(num) or 1
|
||||
self:UpdateSubMaterialId()
|
||||
end
|
||||
|
||||
function PART:FixMaterial()
|
||||
local mat = self.Materialm
|
||||
if not mat then return end
|
||||
|
||||
local shader = mat:GetShader()
|
||||
if shader ~= "UnlitGeneric" then return end
|
||||
local tex_path = mat:GetString("$basetexture")
|
||||
|
||||
if not tex_path then return end
|
||||
local params = {}
|
||||
|
||||
params["$basetexture"] = tex_path
|
||||
params["$vertexcolor"] = 1
|
||||
params["$additive"] = 1
|
||||
|
||||
self.Materialm = pac.CreateMaterial('pac_submat_fix_' .. pac.Hash(mat:GetName()), "VertexLitGeneric", params)
|
||||
end
|
||||
|
||||
function PART:UrlTextHandler()
|
||||
return function(...)
|
||||
return self:Handleurltex(...)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:Handleurltex(mat, tex)
|
||||
if not IsValid(self) then return end
|
||||
if not mat or mat:IsError() or tex:IsError() then self.Materialm = nil return end
|
||||
|
||||
self.Materialm = mat
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
|
||||
self:UpdateSubMaterialId()
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var, self:UrlTextHandler()) then
|
||||
if var == "" then
|
||||
self.Materialm = nil
|
||||
else
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:FixMaterial()
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
end
|
||||
|
||||
self.Material = var
|
||||
self:UpdateSubMaterialId()
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
local ent = self:GetOwner()
|
||||
|
||||
if ent:IsValid() then
|
||||
self:UpdateSubMaterialId()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide(force)
|
||||
if self.DefaultOnHide or force then
|
||||
self:UpdateSubMaterialId(nil, "")
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
self:OnHide(true)
|
||||
end
|
||||
|
||||
function PART:OnUnParent(part)
|
||||
if not part:IsValid() then return end
|
||||
self:OnHide(true)
|
||||
end
|
||||
|
||||
function PART:Clear()
|
||||
self:RemoveChildren()
|
||||
self:UpdateSubMaterialId(nil, "")
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
52
lua/pac3/core/client/parts/sunbeams.lua
Normal file
52
lua/pac3/core/client/parts/sunbeams.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--[[
|
||||
| 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 ScrW = ScrW
|
||||
local ScrH = ScrH
|
||||
local DrawSunbeams
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.ClassName = "sunbeams"
|
||||
PART.Group = 'effects'
|
||||
PART.Icon = 'icon16/weather_sun.png'
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
BUILDER:GetSet("Darken", 0)
|
||||
BUILDER:GetSet("Multiplier", 0.25, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Size", 0.1, {editor_sensitivity = 0.25})
|
||||
BUILDER:GetSet("Translucent", true)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local mult = self:GetMultiplier()
|
||||
return mult > 0 and "bright sunbeams" or mult < 0 and "dark sunbeams" or self.ClassName
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
if not DrawSunbeams then DrawSunbeams = _G.DrawSunbeams end
|
||||
|
||||
cam.Start2D()
|
||||
local pos = self:GetDrawPosition()
|
||||
local spos = pos:ToScreen()
|
||||
|
||||
local dist_mult = - math.Clamp(pac.EyePos:Distance(pos) / 1000, 0, 1) + 1
|
||||
|
||||
DrawSunbeams(
|
||||
self.Darken,
|
||||
dist_mult * self.Multiplier * (math.Clamp(pac.EyeAng:Forward():Dot((pos - pac.EyePos):GetNormalized()) - 0.5, 0, 1) * 2) ^ 5,
|
||||
self.Size,
|
||||
spos.x / ScrW(),
|
||||
spos.y / ScrH()
|
||||
)
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
547
lua/pac3/core/client/parts/text.lua
Normal file
547
lua/pac3/core/client/parts/text.lua
Normal file
@@ -0,0 +1,547 @@
|
||||
--[[
|
||||
| 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 cam_Start3D = cam.Start3D
|
||||
local cam_Start3D2D = cam.Start3D2D
|
||||
local EyePos = EyePos
|
||||
local EyeAngles = EyeAngles
|
||||
local draw_SimpleTextOutlined = draw.SimpleTextOutlined
|
||||
local DisableClipping = DisableClipping
|
||||
local render_CullMode = render.CullMode
|
||||
local cam_End3D2D = cam.End3D2D
|
||||
local cam_End3D = cam.End3D
|
||||
--local Text_Align = TEXT_ALIGN_CENTER
|
||||
local surface_SetFont = surface.SetFont
|
||||
local Color = Color
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
local default_fonts = {
|
||||
"BudgetLabel",
|
||||
"CenterPrintText",
|
||||
"ChatFont",
|
||||
"ClientTitleFont",
|
||||
"CloseCaption_Bold",
|
||||
"CloseCaption_BoldItalic",
|
||||
"CloseCaption_Italic",
|
||||
"CloseCaption_Normal",
|
||||
"CreditsLogo",
|
||||
"CreditsOutroLogos",
|
||||
"CreditsOutroText",
|
||||
"CreditsText",
|
||||
"Crosshairs",
|
||||
"DebugFixed",
|
||||
"DebugFixedSmall",
|
||||
"DebugOverlay",
|
||||
"Default",
|
||||
"DefaultFixed",
|
||||
"DefaultFixedDropShadow",
|
||||
"DefaultSmall",
|
||||
"DefaultUnderline",
|
||||
"DefaultVerySmall",
|
||||
"HDRDemoText",
|
||||
"HL2MPTypeDeath",
|
||||
"HudDefault",
|
||||
"HudHintTextLarge",
|
||||
"HudHintTextSmall",
|
||||
"HudNumbers",
|
||||
"HudNumbersGlow",
|
||||
"HudNumbersSmall",
|
||||
"HudSelectionNumbers",
|
||||
"HudSelectionText",
|
||||
"Marlett",
|
||||
"QuickInfo",
|
||||
"TargetID",
|
||||
"TargetIDSmall",
|
||||
"Trebuchet18",
|
||||
"Trebuchet24",
|
||||
"WeaponIcons",
|
||||
"WeaponIconsSelected",
|
||||
"WeaponIconsSmall",
|
||||
"DermaDefault",
|
||||
"DermaDefaultBold",
|
||||
"DermaLarge",
|
||||
"GModNotify",
|
||||
"ScoreboardDefault",
|
||||
"ScoreboardDefaultTitle",
|
||||
"GModToolName",
|
||||
"GModToolSubtitle",
|
||||
"GModToolHelp",
|
||||
"GModToolScreen",
|
||||
"ContentHeader",
|
||||
"GModWorldtip",
|
||||
"ContentHeader",
|
||||
"DefaultBold",
|
||||
"TabLarge",
|
||||
"Trebuchet22",
|
||||
"TraitorState",
|
||||
"TimeLeft",
|
||||
"HealthAmmo",
|
||||
"cool_small",
|
||||
"cool_large",
|
||||
"treb_small"
|
||||
}
|
||||
|
||||
|
||||
PART.ClassName = "text"
|
||||
PART.Group = "effects"
|
||||
PART.Icon = "icon16/text_align_center.png"
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:SetPropertyGroup("generic")
|
||||
:PropertyOrder("Name")
|
||||
:PropertyOrder("Hide")
|
||||
:GetSet("Text", "")
|
||||
:GetSet("Font", "default", {enums = default_fonts})
|
||||
:GetSet("Size", 1, {editor_sensitivity = 0.25})
|
||||
:GetSet("DrawMode", "DrawTextOutlined", {enums = {
|
||||
["draw.SimpleTextOutlined 3D2D"] = "DrawTextOutlined",
|
||||
["draw.SimpleTextOutlined 2D"] = "DrawTextOutlined2D",
|
||||
["surface.DrawText"] = "SurfaceText"
|
||||
}})
|
||||
|
||||
:SetPropertyGroup("text layout")
|
||||
:GetSet("HorizontalTextAlign", TEXT_ALIGN_CENTER, {enums = {["Left"] = "0", ["Center"] = "1", ["Right"] = "2"}})
|
||||
:GetSet("VerticalTextAlign", TEXT_ALIGN_CENTER, {enums = {["Center"] = "1", ["Top"] = "3", ["Bottom"] = "4"}})
|
||||
:GetSet("ConcatenateTextAndOverrideValue",false,{editor_friendly = "CombinedText"})
|
||||
:GetSet("TextPosition","Prefix", {enums = {["Prefix"] = "Prefix", ["Postfix"] = "Postfix"}},{editor_friendly = "ConcatenateMode"})
|
||||
|
||||
:SetPropertyGroup("data source")
|
||||
:GetSet("TextOverride", "Text", {enums = {
|
||||
["Proxy value (DynamicTextValue)"] = "Proxy",
|
||||
["Text"] = "Text",
|
||||
["Health"] = "Health",
|
||||
["Maximum Health"] = "MaxHealth",
|
||||
["Armor"] = "Armor",
|
||||
["Maximum Armor"] = "MaxArmor",
|
||||
["Timerx"] = "Timerx",
|
||||
["CurTime"] = "CurTime",
|
||||
["RealTime"] = "RealTime",
|
||||
["Velocity"] = "Velocity",
|
||||
["Velocity Vector"] = "VelocityVector",
|
||||
["Position Vector"] = "PositionVector",
|
||||
["Owner Position Vector"] = "OwnerPositionVector",
|
||||
["Clip current Ammo"] = "Ammo",
|
||||
["Clip Size"] = "ClipSize",
|
||||
["Ammo Reserve"] = "AmmoReserve",
|
||||
["Sequence Name"] = "SequenceName",
|
||||
["Weapon Name"] = "Weapon",
|
||||
["Vehicle Class"] = "VehicleClass",
|
||||
["Model Name"] = "ModelName",
|
||||
["Model Path"] = "ModelPath",
|
||||
["Player Name"] = "PlayerName",
|
||||
["Player SteamID"] = "SteamID",
|
||||
["Map"] = "Map",
|
||||
["Ground Surface"] = "GroundSurface",
|
||||
["Ground Entity Class"] = "GroundEntityClass",
|
||||
["Players"] = "Players",
|
||||
["Max Players"] = "MaxPlayers",
|
||||
["Difficulty"] = "GameDifficulty"
|
||||
}})
|
||||
:GetSet("DynamicTextValue", 0)
|
||||
:GetSet("RoundingPosition", 2, {editor_onchange = function(self, num)
|
||||
return math.Round(num,0)
|
||||
end})
|
||||
|
||||
:SetPropertyGroup("orientation")
|
||||
:PropertyOrder("AimPartName")
|
||||
:PropertyOrder("Bone")
|
||||
:PropertyOrder("Position")
|
||||
:SetPropertyGroup("appearance")
|
||||
BUILDER:GetSet("ForceAdditive",false, {description = "additive rendering for the surface.DrawText mode"})
|
||||
BUILDER:GetSet("Outline", 0)
|
||||
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
|
||||
BUILDER:GetSet("OutlineColor", Vector(255, 255, 255), {editor_panel = "color"})
|
||||
BUILDER:GetSet("OutlineAlpha", 1, {editor_onchange = function(self, num)
|
||||
self.sens = 0.25
|
||||
num = tonumber(num)
|
||||
return math.Clamp(num, 0, 1)
|
||||
end})
|
||||
BUILDER:GetSet("Translucent", true)
|
||||
:SetPropertyGroup("CustomFont")
|
||||
:GetSet("CreateCustomFont",false, {description = "Tries to create a custom font.\nHeavily throttled as creating fonts is an expensive process.\nSupport is limited because of the fonts' supported features and the limits of Lua strings.\nFont names include those stored in your operating system. for example: Comic Sans MS, Ink Free"})
|
||||
:GetSet("CustomFont", "DermaDefault")
|
||||
:GetSet("FontSize", 13)
|
||||
:GetSet("FontWeight",500)
|
||||
:GetSet("FontBlurSize",0)
|
||||
:GetSet("FontScanLines",0)
|
||||
:GetSet("FontAntialias",true)
|
||||
:GetSet("FontUnderline",false)
|
||||
:GetSet("FontItalic",false)
|
||||
:GetSet("FontStrikeout",false)
|
||||
:GetSet("FontSymbol",false)
|
||||
:GetSet("FontRotary",false)
|
||||
:GetSet("Shadow",false)
|
||||
:GetSet("FontAdditive",false)
|
||||
:GetSet("FontOutline",false)
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
if self.TextOverride ~= "Text" then return self.TextOverride end
|
||||
|
||||
return 'Text: "' .. self:GetText() .. '"'
|
||||
end
|
||||
|
||||
function PART:SetColor(v)
|
||||
self.ColorC = self.ColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.ColorC.r = v.r
|
||||
self.ColorC.g = v.g
|
||||
self.ColorC.b = v.b
|
||||
|
||||
self.Color = v
|
||||
end
|
||||
|
||||
function PART:SetAlpha(n)
|
||||
self.ColorC = self.ColorC or Color(255, 255, 255, 255)
|
||||
self.ColorC.a = n * 255
|
||||
|
||||
self.Alpha = n
|
||||
end
|
||||
|
||||
function PART:SetOutlineColor(v)
|
||||
self.OutlineColorC = self.OutlineColorC or Color(255, 255, 255, 255)
|
||||
|
||||
self.OutlineColorC.r = v.r
|
||||
self.OutlineColorC.g = v.g
|
||||
self.OutlineColorC.b = v.b
|
||||
|
||||
self.OutlineColor = v
|
||||
end
|
||||
|
||||
function PART:SetOutlineAlpha(n)
|
||||
self.OutlineColorC = self.OutlineColorC or Color(255, 255, 255, 255)
|
||||
self.OutlineColorC.a = n * 255
|
||||
|
||||
self.OutlineAlpha = n
|
||||
end
|
||||
|
||||
function PART:SetFont(str)
|
||||
self.UsedFont = str
|
||||
if not self.CreateCustomFont then
|
||||
if not pcall(surface_SetFont, str) then
|
||||
if #self.Font > 20 then
|
||||
|
||||
self.lastwarn = self.lastwarn or CurTime()
|
||||
if self.lastwarn > CurTime() + 1 then
|
||||
pac.Message(Color(255,150,0),str.." Font not found! Could be custom font, trying again in 4 seconds!")
|
||||
self.lastwarn = CurTime()
|
||||
end
|
||||
timer.Simple(4, function()
|
||||
if not pcall(surface_SetFont, str) then
|
||||
pac.Message(Color(255,150,0),str.." Font still not found! Reverting to DermaDefault!")
|
||||
str = "DermaDefault"
|
||||
self.UsedFont = str
|
||||
end
|
||||
end)
|
||||
else
|
||||
timer.Simple(5, function()
|
||||
if not pcall(surface_SetFont, str) then
|
||||
pac.Message(Color(255,150,0),str.." Font still not found! Reverting to DermaDefault!")
|
||||
str = "DermaDefault"
|
||||
self.UsedFont = str
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.Font = self.UsedFont
|
||||
end
|
||||
local lastfontcreationtime = 0
|
||||
function PART:OnDraw()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
self:CheckFont()
|
||||
if not pcall(surface_SetFont, self.UsedFont) then return end
|
||||
|
||||
local DisplayText = self.Text or ""
|
||||
if self.TextOverride == "Text" then goto DRAW end
|
||||
DisplayText = ""
|
||||
if self.TextOverride == "Health" then DisplayText = self:GetRootPart():GetOwner():Health()
|
||||
elseif self.TextOverride == "MaxHealth" then
|
||||
DisplayText = self:GetRootPart():GetOwner():GetMaxHealth()
|
||||
elseif self.TextOverride == "Ammo" then
|
||||
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetActiveWeapon():Clip1() or ""
|
||||
elseif self.TextOverride == "ClipSize" then
|
||||
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetActiveWeapon():GetMaxClip1() or ""
|
||||
elseif self.TextOverride == "AmmoReserve" then
|
||||
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetAmmoCount(self:GetPlayerOwner():GetActiveWeapon():GetPrimaryAmmoType()) or ""
|
||||
elseif self.TextOverride == "Armor" then
|
||||
DisplayText = self:GetPlayerOwner():Armor()
|
||||
elseif self.TextOverride == "MaxArmor" then
|
||||
DisplayText = self:GetPlayerOwner():GetMaxArmor()
|
||||
elseif self.TextOverride == "Timerx" then
|
||||
DisplayText = ""..math.Round(CurTime() - self.time,self.RoundingPosition)
|
||||
elseif self.TextOverride == "CurTime" then
|
||||
DisplayText = ""..math.Round(CurTime(),self.RoundingPosition)
|
||||
elseif self.TextOverride == "RealTime" then
|
||||
DisplayText = ""..math.Round(RealTime(),self.RoundingPosition)
|
||||
elseif self.TextOverride == "Velocity" then
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
DisplayText = math.Round(ent:GetVelocity():Length(),2)
|
||||
elseif self.TextOverride == "VelocityVector" then
|
||||
local ent = self:GetOwner() or self:GetRootPart():GetOwner()
|
||||
local vec = ent:GetVelocity()
|
||||
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
|
||||
elseif self.TextOverride == "PositionVector" then
|
||||
local vec = self:GetDrawPosition()
|
||||
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
|
||||
elseif self.TextOverride == "OwnerPositionVector" then
|
||||
local ent = self:GetRootPart():GetOwner()
|
||||
local vec = ent:GetPos()
|
||||
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
|
||||
elseif self.TextOverride == "SequenceName" then
|
||||
DisplayText = self:GetRootPart():GetOwner():GetSequenceName(self:GetPlayerOwner():GetSequence())
|
||||
elseif self.TextOverride == "PlayerName" then
|
||||
DisplayText = self:GetPlayerOwner():GetName()
|
||||
elseif self.TextOverride == "SteamID" then
|
||||
DisplayText = self:GetPlayerOwner():SteamID()
|
||||
elseif self.TextOverride == "ModelName" then
|
||||
local path = self:GetRootPart():GetOwner():GetModel() or "null"
|
||||
path = string.Split(path, "/")[#string.Split(path, "/")]
|
||||
path = string.gsub(path,".mdl","")
|
||||
DisplayText = path
|
||||
elseif self.TextOverride == "ModelPath" then
|
||||
DisplayText = self:GetPlayerOwner():GetModel()
|
||||
elseif self.TextOverride == "Map" then
|
||||
DisplayText = game.GetMap()
|
||||
elseif self.TextOverride == "GroundSurface" then
|
||||
local trace = util.TraceLine( {
|
||||
start = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, 30),
|
||||
endpos = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, -60 ),
|
||||
filter = function(ent)
|
||||
if ent == self:GetRootPart():GetOwner() or ent == self:GetPlayerOwner() then return false else return true end
|
||||
end
|
||||
})
|
||||
if trace.Hit then
|
||||
if trace.MatType == MAT_ANTLION then DisplayText = "Antlion"
|
||||
elseif trace.MatType == MAT_BLOODYFLESH then DisplayText = "Bloody Flesh"
|
||||
elseif trace.MatType == MAT_CONCRETE then DisplayText = "Concrete"
|
||||
elseif trace.MatType == MAT_DIRT then DisplayText = "Dirt"
|
||||
elseif trace.MatType == MAT_EGGSHELL then DisplayText = "Egg Shell"
|
||||
elseif trace.MatType == MAT_FLESH then DisplayText = "Flesh"
|
||||
elseif trace.MatType == MAT_GRATE then DisplayText = "Grate"
|
||||
elseif trace.MatType == MAT_ALIENFLESH then DisplayText = "Alien Flesh"
|
||||
elseif trace.MatType == MAT_CLIP then DisplayText = "Clip"
|
||||
elseif trace.MatType == MAT_SNOW then DisplayText = "Snow"
|
||||
elseif trace.MatType == MAT_PLASTIC then DisplayText = "Plastic"
|
||||
elseif trace.MatType == MAT_METAL then DisplayText = "Metal"
|
||||
elseif trace.MatType == MAT_SAND then DisplayText = "Sand"
|
||||
elseif trace.MatType == MAT_FOLIAGE then DisplayText = "Foliage"
|
||||
elseif trace.MatType == MAT_COMPUTER then DisplayText = "Computer"
|
||||
elseif trace.MatType == MAT_SLOSH then DisplayText = "Slime"
|
||||
elseif trace.MatType == MAT_TILE then DisplayText = "Tile"
|
||||
elseif trace.MatType == MAT_GRASS then DisplayText = "Grass"
|
||||
elseif trace.MatType == MAT_VENT then DisplayText = "Grass"
|
||||
elseif trace.MatType == MAT_WOOD then DisplayText = "Wood"
|
||||
elseif trace.MatType == MAT_DEFAULT then DisplayText = "Default"
|
||||
elseif trace.MatType == MAT_GLASS then DisplayText = "Glass"
|
||||
elseif trace.MatType == MAT_WARPSHIELD then DisplayText = "Warp Shield"
|
||||
else DisplayText = "Other Surface" end
|
||||
else DisplayText = "Air" end
|
||||
elseif self.TextOverride == "GroundEntityClass" then
|
||||
local trace = util.TraceLine( {
|
||||
start = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, 30),
|
||||
endpos = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, -60 ),
|
||||
filter = function(ent)
|
||||
if ent == self:GetRootPart():GetOwner() or ent == self:GetPlayerOwner() then return false else return true end
|
||||
end
|
||||
})
|
||||
if trace.Hit then
|
||||
DisplayText = trace.Entity:GetClass()
|
||||
end
|
||||
elseif self.TextOverride == "GameDifficulty" then
|
||||
local diff = game.GetSkillLevel()
|
||||
if diff == 1 then DisplayText = "Easy"
|
||||
elseif diff == 2 then DisplayText = "Normal"
|
||||
elseif diff == 3 then DisplayText = "Hard" end
|
||||
elseif self.TextOverride == "Players" then
|
||||
DisplayText = #player.GetAll()
|
||||
elseif self.TextOverride == "MaxPlayers" then
|
||||
DisplayText = game.MaxPlayers()
|
||||
elseif self.TextOverride == "Weapon" then
|
||||
if IsValid(self:GetPlayerOwner():GetActiveWeapon()) then
|
||||
DisplayText = self:GetPlayerOwner():GetActiveWeapon():GetClass()
|
||||
else DisplayText = "unarmed" end
|
||||
elseif self.TextOverride == "VehicleClass" then
|
||||
if IsValid(self:GetPlayerOwner():GetVehicle()) then
|
||||
DisplayText = self:GetPlayerOwner():GetVehicle():GetClass()
|
||||
else DisplayText = "not driving" end
|
||||
elseif self.TextOverride == "Proxy" then
|
||||
DisplayText = ""..math.Round(self.DynamicTextValue,self.RoundingPosition)
|
||||
end
|
||||
|
||||
if self.ConcatenateTextAndOverrideValue then
|
||||
if self.TextPosition == "Prefix" then
|
||||
DisplayText = ""..self.Text..DisplayText
|
||||
elseif self.TextPosition == "Postfix" then
|
||||
DisplayText = ""..DisplayText..self.Text
|
||||
end
|
||||
end
|
||||
|
||||
::DRAW::
|
||||
|
||||
if DisplayText ~= "" then
|
||||
if self.DrawMode == "DrawTextOutlined" then
|
||||
cam_Start3D(EyePos(), EyeAngles())
|
||||
cam_Start3D2D(pos, ang, self.Size)
|
||||
local oldState = DisableClipping(true)
|
||||
|
||||
draw_SimpleTextOutlined(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
|
||||
render_CullMode(1) -- MATERIAL_CULLMODE_CW
|
||||
|
||||
draw_SimpleTextOutlined(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
|
||||
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
|
||||
|
||||
DisableClipping(oldState)
|
||||
cam_End3D2D()
|
||||
cam_End3D()
|
||||
cam_Start3D(EyePos(), EyeAngles())
|
||||
cam_Start3D2D(pos, ang, self.Size)
|
||||
local oldState = DisableClipping(true)
|
||||
|
||||
draw.SimpleText(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
|
||||
render_CullMode(1) -- MATERIAL_CULLMODE_CW
|
||||
|
||||
draw.SimpleText(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
|
||||
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
|
||||
|
||||
DisableClipping(oldState)
|
||||
cam_End3D2D()
|
||||
cam_End3D()
|
||||
elseif self.DrawMode == "SurfaceText" or self.DrawMode == "DrawTextOutlined2D" then
|
||||
hook.Add("HUDPaint", "pac.DrawText"..self.UniqueID, function()
|
||||
if not pcall(surface_SetFont, self.UsedFont) then return end
|
||||
self:SetFont(self.UsedFont)
|
||||
|
||||
surface.SetTextColor(self.Color.r, self.Color.g, self.Color.b, 255*self.Alpha)
|
||||
|
||||
surface.SetFont(self.UsedFont)
|
||||
local pos2d = self:GetDrawPosition():ToScreen()
|
||||
local w, h = surface.GetTextSize(DisplayText)
|
||||
|
||||
if self.HorizontalTextAlign == 0 then --left
|
||||
pos2d.x = pos2d.x
|
||||
elseif self.HorizontalTextAlign == 1 then --center
|
||||
pos2d.x = pos2d.x - w/2
|
||||
elseif self.HorizontalTextAlign == 2 then --right
|
||||
pos2d.x = pos2d.x - w
|
||||
end
|
||||
|
||||
if self.VerticalTextAlign == 1 then --center
|
||||
pos2d.y = pos2d.y - h/2
|
||||
elseif self.VerticalTextAlign == 3 then --top
|
||||
pos2d.y = pos2d.y
|
||||
elseif self.VerticalTextAlign == 4 then --bottom
|
||||
pos2d.y = pos2d.y - h
|
||||
end
|
||||
|
||||
surface.SetTextPos(pos2d.x, pos2d.y)
|
||||
local dist = (EyePos() - self:GetWorldPosition()):Length()
|
||||
local fadestartdist = 200
|
||||
local fadeenddist = 1000
|
||||
if fadestartdist == 0 then fadestartdist = 0.1 end
|
||||
if fadeenddist == 0 then fadeenddist = 0.1 end
|
||||
|
||||
if fadestartdist > fadeenddist then
|
||||
local temp = fadestartdist
|
||||
fadestartdist = fadeenddist
|
||||
fadeenddist = temp
|
||||
end
|
||||
|
||||
if dist < fadeenddist then
|
||||
if dist < fadestartdist then
|
||||
if self.DrawMode == "DrawTextOutlined2D" then
|
||||
draw.SimpleTextOutlined(DisplayText, self.UsedFont, pos2d.x, pos2d.y, Color(self.Color.r,self.Color.g,self.Color.b,255*self.Alpha), TEXT_ALIGN_TOP, TEXT_ALIGN_LEFT, self.Outline, Color(self.OutlineColor.r,self.OutlineColor.g,self.OutlineColor.b, 255*self.OutlineAlpha))
|
||||
elseif self.DrawMode == "SurfaceText" then
|
||||
surface.DrawText(DisplayText, self.ForceAdditive)
|
||||
end
|
||||
|
||||
else
|
||||
local fade = math.pow(math.Clamp(1 - (dist-fadestartdist)/fadeenddist,0,1),3)
|
||||
|
||||
if self.DrawMode == "DrawTextOutlined2D" then
|
||||
draw.SimpleTextOutlined(DisplayText, self.UsedFont, pos2d.x, pos2d.y, Color(self.Color.r,self.Color.g,self.Color.b,255*self.Alpha*fade), TEXT_ALIGN_TOP, TEXT_ALIGN_LEFT, self.Outline, Color(self.OutlineColor.r,self.OutlineColor.g,self.OutlineColor.b, 255*self.OutlineAlpha*fade))
|
||||
elseif self.DrawMode == "SurfaceText" then
|
||||
surface.SetTextColor(self.Color.r * fade, self.Color.g * fade, self.Color.b * fade)
|
||||
surface.DrawText(DisplayText, true)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
if self.DrawMode == "DrawTextOutlined" then
|
||||
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
|
||||
end
|
||||
else hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID) end
|
||||
end
|
||||
|
||||
function PART:Initialize()
|
||||
self:TryCreateFont()
|
||||
end
|
||||
|
||||
function PART:CheckFont()
|
||||
if self.CreateCustomFont then
|
||||
lastfontcreationtime = lastfontcreationtime or 0
|
||||
if lastfontcreationtime + 3 <= CurTime() then
|
||||
self:TryCreateFont()
|
||||
end
|
||||
else
|
||||
self:SetFont(self.Font)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PART:TryCreateFont()
|
||||
if "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID == self.lastcustomfont then
|
||||
self.UsedFont = "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID
|
||||
return
|
||||
end
|
||||
if self.CreateCustomFont then
|
||||
local newfont = "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID
|
||||
surface.CreateFont( newfont, {
|
||||
font = self.CustomFont, -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
|
||||
extended = self.Extended,
|
||||
size = self.FontSize,
|
||||
weight = self.Weight,
|
||||
blursize = self.BlurSize,
|
||||
scanlines = self.ScanLines,
|
||||
antialias = self.Antialias,
|
||||
underline = self.Underline,
|
||||
italic = self.Italic,
|
||||
strikeout = self.Strikeout,
|
||||
symbol = self.Symbol,
|
||||
rotary = self.Rotary,
|
||||
shadow = self.Shadow,
|
||||
additive = self.Additive,
|
||||
outline = self.Outline,
|
||||
} )
|
||||
self:SetFont(newfont)
|
||||
self.lastcustomfont = newfont
|
||||
lastfontcreationtime = CurTime()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self.time = CurTime()
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
|
||||
end
|
||||
function PART:OnRemove()
|
||||
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
|
||||
end
|
||||
function PART:SetText(str)
|
||||
self.Text = str
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
195
lua/pac3/core/client/parts/trail.lua
Normal file
195
lua/pac3/core/client/parts/trail.lua
Normal file
@@ -0,0 +1,195 @@
|
||||
--[[
|
||||
| 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 Lerp = Lerp
|
||||
local tonumber = tonumber
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
local math_ceil = math.ceil
|
||||
local math_abs = math.abs
|
||||
local math_min = math.min
|
||||
local render_StartBeam = render.StartBeam
|
||||
local cam_IgnoreZ = cam.IgnoreZ
|
||||
local render_EndBeam = render.EndBeam
|
||||
local render_AddBeam = render.AddBeam
|
||||
local render_SetMaterial = render.SetMaterial
|
||||
local Vector = Vector
|
||||
local RealTime = RealTime
|
||||
|
||||
local temp_color = Color(255, 255, 255)
|
||||
|
||||
function pac.DrawTrail(self, len, spc, pos, ang, mat, scr,scg,scb,sca, ecr,ecg,ecb,eca, start_size, end_size, stretch)
|
||||
self.trail_points = self.trail_points or {}
|
||||
local points = self.trail_points
|
||||
|
||||
if pac.drawing_motionblur_alpha then
|
||||
local a = pac.drawing_motionblur_alpha
|
||||
self.trail_points_motionblur = self.trail_points_motionblur or {}
|
||||
self.trail_points_motionblur[a] = self.trail_points_motionblur[a] or {}
|
||||
points = self.trail_points_motionblur[a]
|
||||
end
|
||||
|
||||
local time = RealTime()
|
||||
|
||||
if not points[1] or points[#points].pos:Distance(pos) > spc then
|
||||
table_insert(points, {pos = pos * 1, life_time = time + len})
|
||||
end
|
||||
|
||||
local count = #points
|
||||
|
||||
render_SetMaterial(mat)
|
||||
|
||||
render_StartBeam(count)
|
||||
for i = #points, 1, -1 do
|
||||
local data = points[i]
|
||||
|
||||
local f = (data.life_time - time)/len
|
||||
local f2 = f
|
||||
f = -f+1
|
||||
|
||||
local coord = (1 / count) * (i - 1)
|
||||
|
||||
temp_color.r = math_min(Lerp(coord, ecr, scr), 255)
|
||||
temp_color.g = math_min(Lerp(coord, ecg, scg), 255)
|
||||
temp_color.b = math_min(Lerp(coord, ecb, scb), 255)
|
||||
temp_color.a = math_min(Lerp(coord, eca, sca), 255)
|
||||
|
||||
render_AddBeam(data.pos, (f * start_size) + (f2 * end_size), coord * stretch, temp_color)
|
||||
|
||||
if f >= 1 then
|
||||
table_remove(points, i)
|
||||
end
|
||||
end
|
||||
render_EndBeam()
|
||||
|
||||
if self.CenterAttraction ~= 0 then
|
||||
local attraction = FrameTime() * self.CenterAttraction
|
||||
local center = Vector(0,0,0)
|
||||
for _, data in ipairs(points) do
|
||||
center:Zero()
|
||||
for _, data in ipairs(points) do
|
||||
center:Add(data.pos)
|
||||
end
|
||||
center:Mul(1 / #points)
|
||||
center:Sub(data.pos)
|
||||
center:Mul(attraction)
|
||||
|
||||
data.pos:Add(center)
|
||||
end
|
||||
end
|
||||
|
||||
if not self.Gravity:IsZero() then
|
||||
local gravity = self.Gravity * FrameTime()
|
||||
gravity:Rotate(ang)
|
||||
for _, data in ipairs(points) do
|
||||
data.pos:Add(gravity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "trail"
|
||||
PART.ClassName = "trail2"
|
||||
PART.Icon = 'icon16/arrow_undo.png'
|
||||
PART.Group = 'effects'
|
||||
PART.ProperColorRange = true
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
:GetSet("Duration", 1)
|
||||
:GetSet("Spacing", 0.25)
|
||||
:GetSet("StartSize", 3)
|
||||
:GetSet("EndSize", 0)
|
||||
:GetSet("StartColor", Vector(1, 1, 1), {editor_panel = "color2"})
|
||||
:GetSet("EndColor", Vector(1, 1, 1), {editor_panel = "color2"})
|
||||
:GetSet("StartAlpha", 1)
|
||||
:GetSet("EndAlpha", 0)
|
||||
:GetSet("Stretch", 1)
|
||||
:GetSet("CenterAttraction", 0)
|
||||
:GetSet("Gravity", Vector(0,0,0))
|
||||
:GetSet("IgnoreZ", false)
|
||||
:GetSet("TrailPath", "trails/laser", {editor_panel = "material"})
|
||||
:GetSet("Translucent", true)
|
||||
:EndStorableVars()
|
||||
|
||||
function PART:GetNiceName()
|
||||
local str = pac.PrettifyName("/" .. self:GetTrailPath())
|
||||
local matched = str and str:match(".+/(.+)")
|
||||
return matched and matched:gsub("%..+", "") or "error"
|
||||
end
|
||||
|
||||
PART.LastAdd = 0
|
||||
|
||||
function PART:Initialize()
|
||||
self:SetTrailPath(self.TrailPath)
|
||||
end
|
||||
|
||||
function PART:SetTrailPath(var)
|
||||
self.TrailPath = var
|
||||
self:SetMaterial(var)
|
||||
end
|
||||
|
||||
function PART:SetMaterial(var)
|
||||
var = var or ""
|
||||
|
||||
if not pac.Handleurltex(self, var, function(mat)
|
||||
self.Materialm = mat
|
||||
self:MakeMaterialUnlit()
|
||||
end) then
|
||||
if isstring(var) then
|
||||
self.Materialm = pac.Material(var, self)
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
elseif type(var) == "IMaterial" then
|
||||
self.Materialm = var
|
||||
self:CallRecursive("OnMaterialChanged")
|
||||
end
|
||||
self:MakeMaterialUnlit()
|
||||
end
|
||||
end
|
||||
|
||||
function PART:MakeMaterialUnlit()
|
||||
if not self.Materialm then return end
|
||||
|
||||
local shader = self.Materialm:GetShader()
|
||||
if shader == "VertexLitGeneric" or shader == "Cable" or shader == "LightmappedGeneric" then
|
||||
self.Materialm = pac.MakeMaterialUnlitGeneric(self.Materialm, self.Id)
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnShow()
|
||||
self.points = {}
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
self.points = {}
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
local pos, ang = self:GetDrawPosition()
|
||||
local mat = self.material_override and self.material_override[0][1] and self.material_override[0][1]:GetRawMaterial() or self.Materialm
|
||||
if not mat then return end
|
||||
pac.DrawTrail(
|
||||
self,
|
||||
math.min(self.Duration, 10),
|
||||
self.Spacing + (self.StartSize/10),
|
||||
pos,
|
||||
ang,
|
||||
mat,
|
||||
|
||||
self.StartColor.x*255, self.StartColor.y*255, self.StartColor.z*255,self.StartAlpha*255,
|
||||
self.EndColor.x*255, self.EndColor.y*255, self.EndColor.z*255,self.EndAlpha*255,
|
||||
|
||||
self.StartSize,
|
||||
self.EndSize,
|
||||
1/self.Stretch
|
||||
)
|
||||
end
|
||||
|
||||
BUILDER:Register()
|
||||
277
lua/pac3/core/client/test.lua
Normal file
277
lua/pac3/core/client/test.lua
Normal file
@@ -0,0 +1,277 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- the order of tests is important, smoke test should always be first
|
||||
local tests = {
|
||||
"smoke",
|
||||
"all_parts",
|
||||
"base_part",
|
||||
"events",
|
||||
"model_modifier",
|
||||
"size_modifier",
|
||||
}
|
||||
|
||||
local COLOR_ERROR = Color(255,100,100)
|
||||
local COLOR_WARNING = Color(255,150,50)
|
||||
local COLOR_NORMAL = Color(255,255,255)
|
||||
local COLOR_OK = Color(150,255,50)
|
||||
|
||||
-- see bottom of this file and test_suite_backdoor.lua on server for more info
|
||||
local run_lua_on_server
|
||||
|
||||
local function msg_color(color, ...)
|
||||
local tbl = {}
|
||||
|
||||
for i = 1, select("#", ...) do
|
||||
local val = select(i, ...)
|
||||
if IsColor(val) then
|
||||
tbl[i] = val
|
||||
else
|
||||
tbl[i] = tostring(val)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(tbl, "\n")
|
||||
|
||||
MsgC(color, unpack(tbl))
|
||||
end
|
||||
|
||||
local function msg_error(...)
|
||||
msg_color(COLOR_ERROR, ...)
|
||||
end
|
||||
|
||||
local function msg_warning(...)
|
||||
msg_color(COLOR_WARNING, ...)
|
||||
end
|
||||
|
||||
local function msg_ok(...)
|
||||
msg_color(COLOR_OK, ...)
|
||||
end
|
||||
|
||||
local function msg(...)
|
||||
msg_color(COLOR_NORMAL, ...)
|
||||
end
|
||||
|
||||
local function start_test(name, done)
|
||||
local test = {}
|
||||
|
||||
local function msg_error(...)
|
||||
test.error_called = true
|
||||
msg_color(COLOR_ERROR, ...)
|
||||
end
|
||||
|
||||
local function msg_warning(...)
|
||||
test.warning_called = true
|
||||
msg_color(COLOR_WARNING, ...)
|
||||
end
|
||||
|
||||
test.name = name
|
||||
test.time = os.clock() + 5
|
||||
|
||||
test.RunLuaOnServer = function(code)
|
||||
local ret
|
||||
run_lua_on_server(code, function(...) ret = {...} end)
|
||||
while not ret do
|
||||
coroutine.yield()
|
||||
end
|
||||
return unpack(ret)
|
||||
end
|
||||
|
||||
function test.SetTestTimeout(sec)
|
||||
test.time = os.clock() + sec
|
||||
end
|
||||
|
||||
function test.Setup()
|
||||
hook.Add("ShouldDrawLocalPlayer", "pac_test", function() return true end)
|
||||
end
|
||||
|
||||
function test.Teardown()
|
||||
hook.Remove("ShouldDrawLocalPlayer", "pac_test")
|
||||
end
|
||||
|
||||
function test.Run(done) error("test.Run is not defined") end
|
||||
function test.Remove()
|
||||
hook.Remove("ShouldDrawLocalPlayer", "pac_test")
|
||||
hook.Remove("Think", "pac_test_coroutine")
|
||||
|
||||
if test.done then return end
|
||||
|
||||
if test.events_consume and test.events_consume_index then
|
||||
msg_error(test.name .. " finished before consuming event ", test.events_consume[test.events_consume_index], " at index ", test.events_consume_index)
|
||||
end
|
||||
|
||||
test.co = nil
|
||||
|
||||
test.Teardown()
|
||||
done(test)
|
||||
|
||||
test.done = true
|
||||
end
|
||||
|
||||
function test.equal(a, b)
|
||||
if a ~= b then
|
||||
msg_error("=============")
|
||||
msg_error("expected ", COLOR_NORMAL, tostring(a), COLOR_ERROR, " got ", COLOR_NORMAL, tostring(b), COLOR_ERROR, "!")
|
||||
msg(debug.traceback())
|
||||
msg_error("=============")
|
||||
end
|
||||
end
|
||||
|
||||
function test.EventConsumer(events)
|
||||
test.events_consume = events
|
||||
test.events_consume_index = 1
|
||||
return function(got)
|
||||
if not test.events_consume_index then
|
||||
msg_error("tried to consume event when finished")
|
||||
msg(debug.traceback())
|
||||
return
|
||||
end
|
||||
|
||||
local expected = events[test.events_consume_index]
|
||||
test.equal(expected, got)
|
||||
test.events_consume_index = test.events_consume_index + 1
|
||||
if not events[test.events_consume_index] then
|
||||
test.events_consume_index = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local env = {}
|
||||
env.test = test
|
||||
env.msg = msg
|
||||
env.msg_ok = msg_ok
|
||||
env.msg_error = msg_error
|
||||
env.msg_warning = msg_warning
|
||||
env.msg_color = msg_color
|
||||
env.yield = coroutine.yield
|
||||
|
||||
local func = CompileFile("pac3/core/client/tests/"..name..".lua")
|
||||
|
||||
if not func then
|
||||
return
|
||||
end
|
||||
|
||||
setfenv(func, setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
if env[key] then
|
||||
return env[key]
|
||||
end
|
||||
return rawget(_G, key)
|
||||
end,
|
||||
}))
|
||||
|
||||
func()
|
||||
|
||||
test.Setup()
|
||||
|
||||
test.co = coroutine.create(function()
|
||||
test.Run(test.Remove)
|
||||
end)
|
||||
|
||||
local ok, err = coroutine.resume(test.co)
|
||||
|
||||
if not ok then
|
||||
ErrorNoHalt(err)
|
||||
test.Remove()
|
||||
end
|
||||
|
||||
hook.Add("Think", "pac_test_coroutine", function()
|
||||
if not test.co then return end
|
||||
|
||||
local ok, err = coroutine.resume(test.co)
|
||||
|
||||
if not ok and err ~= "cannot resume dead coroutine" then
|
||||
ErrorNoHalt(err)
|
||||
test.Remove()
|
||||
end
|
||||
end)
|
||||
|
||||
if test.done then
|
||||
return
|
||||
end
|
||||
|
||||
return test
|
||||
end
|
||||
|
||||
concommand.Add("pac_test", function(ply, _, args)
|
||||
local what = args[1]
|
||||
|
||||
if not what then
|
||||
msg_warning("this command is intended for developers to test that pac works after changing the code")
|
||||
msg_warning("it will remove all parts before starting the test")
|
||||
msg_warning("if you really want to run this command, run 'pac_test client'")
|
||||
return
|
||||
end
|
||||
|
||||
if what == "client" then
|
||||
local which = args[2]
|
||||
|
||||
pac.RemoveAllParts()
|
||||
|
||||
local tests = table.Copy(tests)
|
||||
|
||||
if which then
|
||||
tests = {which}
|
||||
end
|
||||
|
||||
local current_test = nil
|
||||
|
||||
hook.Add("Think", "pac_tests", function()
|
||||
if current_test then
|
||||
if current_test.time < os.clock() then
|
||||
msg_error("test ", current_test.name, " timed out")
|
||||
if current_test.Timeout then
|
||||
local ok, err = pcall(current_test.Timeout)
|
||||
if not ok then
|
||||
msg_error(err)
|
||||
end
|
||||
end
|
||||
current_test.Remove()
|
||||
current_test = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local name = table.remove(tests, 1)
|
||||
if not name then
|
||||
msg("finished testing")
|
||||
hook.Remove("Think", "pac_tests")
|
||||
return
|
||||
end
|
||||
|
||||
msg("running test " .. name)
|
||||
|
||||
current_test = start_test(name, function(test)
|
||||
if not test.warning_called and not test.error_called then
|
||||
msg_ok(name, " - OK")
|
||||
end
|
||||
current_test = nil
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
local lua_server_run_callbacks = {}
|
||||
|
||||
function run_lua_on_server(code, cb)
|
||||
local id = pac.Hash(code .. tostring(cb))
|
||||
lua_server_run_callbacks[id] = cb
|
||||
net.Start("pac3_test_suite_backdoor")
|
||||
net.WriteString(id)
|
||||
net.WriteString(code)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
net.Receive("pac3_test_suite_backdoor_receive_results", function()
|
||||
local id = net.ReadString()
|
||||
local results = net.ReadTable()
|
||||
lua_server_run_callbacks[id](unpack(results))
|
||||
lua_server_run_callbacks[id] = nil
|
||||
end)
|
||||
22
lua/pac3/core/client/tests/all_parts.lua
Normal file
22
lua/pac3/core/client/tests/all_parts.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
function test.Run(done)
|
||||
local root = pac.CreatePart("group")
|
||||
|
||||
for class_name in pairs(pac.GetRegisteredParts()) do
|
||||
root:CreatePart(class_name)
|
||||
end
|
||||
|
||||
timer.Simple(0.5, function()
|
||||
root:Remove()
|
||||
done()
|
||||
end)
|
||||
end
|
||||
71
lua/pac3/core/client/tests/base_part.lua
Normal file
71
lua/pac3/core/client/tests/base_part.lua
Normal file
@@ -0,0 +1,71 @@
|
||||
--[[
|
||||
| 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 consume = test.EventConsumer({
|
||||
"init",
|
||||
"hide",
|
||||
"parent group",
|
||||
"shown from rendering",
|
||||
"draw",
|
||||
"hide",
|
||||
"remove",
|
||||
})
|
||||
|
||||
function test.Run(done)
|
||||
do
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "test"
|
||||
PART.ClassName = "test"
|
||||
PART.Icon = 'icon16/cut.png'
|
||||
|
||||
function PART:Initialize()
|
||||
consume("init")
|
||||
end
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if from_rendering then
|
||||
consume("shown from rendering")
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnDraw()
|
||||
consume("draw")
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function PART:OnThink()
|
||||
--consume("think")
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
consume("hide")
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
consume("remove")
|
||||
done()
|
||||
end
|
||||
|
||||
function PART:OnParent(parent)
|
||||
consume("parent " .. tostring(parent.ClassName))
|
||||
end
|
||||
|
||||
function PART:OnUnparent()
|
||||
consume("unparent")
|
||||
end
|
||||
|
||||
pac.RegisterPart(PART)
|
||||
end
|
||||
|
||||
local root = pac.CreatePart("group")
|
||||
local part = root:CreatePart("test")
|
||||
end
|
||||
114
lua/pac3/core/client/tests/events.lua
Normal file
114
lua/pac3/core/client/tests/events.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
| 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 consume = test.EventConsumer({
|
||||
"shown from rendering",
|
||||
"event think",
|
||||
"event triggers hide",
|
||||
"hidden",
|
||||
"event triggers show",
|
||||
"shown from event",
|
||||
})
|
||||
|
||||
function test.Run(done)
|
||||
local stage = nil
|
||||
|
||||
do
|
||||
local BUILDER, PART = pac.PartTemplate("base_drawable")
|
||||
|
||||
PART.FriendlyName = "test"
|
||||
PART.ClassName = "test"
|
||||
PART.Icon = 'icon16/cut.png'
|
||||
|
||||
function PART:OnShow(from_rendering)
|
||||
if from_rendering then
|
||||
-- TODO: OnShow(true) triggers 2 times
|
||||
if stage == nil then
|
||||
-- 1
|
||||
|
||||
consume("shown from rendering")
|
||||
stage = "first event frame"
|
||||
end
|
||||
else
|
||||
if stage == "wait for trigger" then
|
||||
|
||||
-- 5
|
||||
|
||||
consume("shown from event")
|
||||
self:GetRootPart():Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnHide()
|
||||
if stage == "hide from event" then
|
||||
|
||||
-- 3
|
||||
|
||||
consume("hidden")
|
||||
stage = "show trigger"
|
||||
end
|
||||
end
|
||||
|
||||
function PART:OnRemove()
|
||||
done()
|
||||
end
|
||||
|
||||
pac.RegisterPart(PART)
|
||||
end
|
||||
|
||||
do
|
||||
local event = pac.CreateEvent("test")
|
||||
|
||||
function event:Think(event, ent, ...)
|
||||
|
||||
if stage == "first event frame" then
|
||||
|
||||
-- 2
|
||||
|
||||
consume("event think")
|
||||
stage = "hide trigger"
|
||||
|
||||
|
||||
elseif stage == "hide trigger" then
|
||||
|
||||
|
||||
-- 3
|
||||
|
||||
consume("event triggers hide")
|
||||
stage = "hide from event"
|
||||
|
||||
return true -- hide
|
||||
elseif stage == "show trigger" then
|
||||
|
||||
-- 4
|
||||
|
||||
consume("event triggers show")
|
||||
stage = "wait for trigger"
|
||||
|
||||
|
||||
return false -- show
|
||||
end
|
||||
end
|
||||
|
||||
pac.RegisterEvent(event)
|
||||
end
|
||||
|
||||
local root = pac.CreatePart("group")
|
||||
|
||||
do
|
||||
local event = root:CreatePart("event")
|
||||
event:SetEvent("test")
|
||||
event:SetAffectChildrenOnly(true)
|
||||
|
||||
local child = event:CreatePart("test")
|
||||
end
|
||||
end
|
||||
65
lua/pac3/core/client/tests/model_modifier.lua
Normal file
65
lua/pac3/core/client/tests/model_modifier.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
| 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 function equal(a,b, msg)
|
||||
if a ~= b then
|
||||
error(tostring(a) .. " != " .. tostring(b) .. ": " .. msg .. "\n", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function test.Run(done)
|
||||
local owner = pac.LocalPlayer
|
||||
local prev = owner:GetModel()
|
||||
local mdl = "models/combine_helicopter/helicopter_bomb01.mdl"
|
||||
|
||||
if prev == mdl then
|
||||
owner:SetModel(test.RunLuaOnServer("return Entity(" .. owner:EntIndex() .. "):GetModel()"))
|
||||
prev = owner:GetModel()
|
||||
assert(prev ~= mdl, "something is wrong!!")
|
||||
end
|
||||
|
||||
for _, class in ipairs({"entity", "entity2"}) do
|
||||
local root = pac.CreatePart("group")
|
||||
local entity = root:CreatePart(class)
|
||||
|
||||
-- the owner is not valid right away, when the owner is valid, the changes are applied
|
||||
repeat yield() until entity:GetOwner():IsValid()
|
||||
|
||||
entity:SetModel(mdl)
|
||||
|
||||
equal(owner:GetModel(), mdl, " after "..class..":SetModel")
|
||||
root:Remove()
|
||||
equal(owner:GetModel(), prev, " after root is removed, the model should be reverted")
|
||||
end
|
||||
|
||||
RunConsoleCommand("pac_modifier_model", "1")
|
||||
repeat yield() until GetConVar("pac_modifier_model"):GetBool()
|
||||
|
||||
pac.emut.MutateEntity(owner, "model", owner, mdl)
|
||||
|
||||
equal(test.RunLuaOnServer("return Entity(" .. owner:EntIndex() .. "):GetModel()"), mdl, " server model differs")
|
||||
|
||||
RunConsoleCommand("pac_modifier_model", "0")
|
||||
repeat yield() until not GetConVar("pac_modifier_model"):GetBool()
|
||||
|
||||
equal(test.RunLuaOnServer("return Entity(" .. owner:EntIndex() .. "):GetModel()"), prev, " should be reverted")
|
||||
|
||||
pac.emut.RestoreMutations(owner, "model", owner)
|
||||
|
||||
RunConsoleCommand("pac_modifier_model", "1")
|
||||
repeat yield() until GetConVar("pac_modifier_model"):GetBool()
|
||||
|
||||
done()
|
||||
end
|
||||
|
||||
|
||||
function test.Teardown()
|
||||
timer.Remove("pac_test")
|
||||
end
|
||||
113
lua/pac3/core/client/tests/property_fuzz.lua
Normal file
113
lua/pac3/core/client/tests/property_fuzz.lua
Normal file
@@ -0,0 +1,113 @@
|
||||
--[[
|
||||
| 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 function RandomModel()
|
||||
for _, tbl in RandomPairs(spawnmenu.GetPropTable()) do
|
||||
for _, val in RandomPairs(tbl.contents) do
|
||||
if val.model then
|
||||
return val.model
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RandomMaterial()
|
||||
return table.Random(list.Get("OverrideMaterials"))
|
||||
end
|
||||
|
||||
function test.Run(done)
|
||||
local root = pac.CreatePart("group")
|
||||
local classes = {}
|
||||
for class_name in pairs(pac.GetRegisteredParts()) do
|
||||
local part = root:CreatePart(class_name)
|
||||
table.insert(classes, class_name)
|
||||
end
|
||||
|
||||
local run = true
|
||||
|
||||
timer.Simple(10, function()
|
||||
run = false
|
||||
root:Remove()
|
||||
done()
|
||||
end)
|
||||
|
||||
local file = file.Open("pac_test_log.txt", "w", "DATA")
|
||||
local function log(line)
|
||||
file:Write(line .. "\n")
|
||||
file:Flush()
|
||||
end
|
||||
|
||||
while run do
|
||||
local children = root:GetChildrenList()
|
||||
for _, part in RandomPairs(children) do
|
||||
if part.ClassName == "player_movement" then continue end
|
||||
|
||||
for key, val in RandomPairs(part.StorableVars) do
|
||||
|
||||
if key == "UniqueID" then continue end
|
||||
if key == "Name" then continue end
|
||||
if key == "OwnerName" then continue end
|
||||
if key == "Command" then continue end
|
||||
|
||||
log(part.ClassName .. ".Get" .. key)
|
||||
local val = part["Get"..key](part)
|
||||
|
||||
if key:EndsWith("UID") then
|
||||
if math.random() > 0.5 then
|
||||
val = table.Random(children)
|
||||
else
|
||||
val = part:CreatePart(table.Random(classes))
|
||||
end
|
||||
val = val:GetUniqueID()
|
||||
elseif isnumber(val) then
|
||||
val = math.Rand(-1000, 100)
|
||||
elseif isvector(val) then
|
||||
val = VectorRand()*1000
|
||||
elseif isangle(val) then
|
||||
val = Angle(math.Rand(0, 360), math.Rand(0, 360), math.Rand(0, 360))
|
||||
elseif isbool(val) then
|
||||
val = math.random() > 0.5
|
||||
elseif isstring(val) then
|
||||
|
||||
|
||||
|
||||
local udata = pac.GetPropertyUserdata(part, key)
|
||||
|
||||
if udata then
|
||||
local t = udata.editor_panel
|
||||
if t == "model" then
|
||||
val = RandomModel()
|
||||
elseif t == "material" then
|
||||
val = RandomMaterial()
|
||||
elseif key == "Bone" then
|
||||
for bone in RandomPairs(part:GetModelBones()) do
|
||||
val = bone
|
||||
break
|
||||
end
|
||||
else
|
||||
print(part.ClassName, key, t)
|
||||
val = pac.Hash()
|
||||
end
|
||||
else
|
||||
print(part, key)
|
||||
end
|
||||
end
|
||||
|
||||
log(part.ClassName .. ".Set" .. key .. " = " .. tostring(val))
|
||||
part["Set" .. key](part, val)
|
||||
end
|
||||
yield()
|
||||
test.SetTestTimeout(1)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
76
lua/pac3/core/client/tests/size_modifier.lua
Normal file
76
lua/pac3/core/client/tests/size_modifier.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
--[[
|
||||
| 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 function equal(a,b, msg, level)
|
||||
level = level or 0
|
||||
if a ~= b then
|
||||
error(tostring(a) .. " != " .. tostring(b) .. ": " .. msg, 2 + level)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_shared(owner, what, expect)
|
||||
equal(test.RunLuaOnServer("return Entity(" .. owner:EntIndex() .. "):"..what.."()"), expect, " server mismatch with client", 1)
|
||||
end
|
||||
|
||||
function test.Run(done)
|
||||
local owner = pac.LocalPlayer
|
||||
|
||||
for _, class in ipairs({"entity", "entity2"}) do
|
||||
local root = pac.CreatePart("group")
|
||||
local entity = root:CreatePart(class)
|
||||
|
||||
entity:SetSize(0.5)
|
||||
|
||||
-- the owner is not valid right away, when the owner is valid, the changes are applied
|
||||
repeat yield() until entity:GetOwner():IsValid()
|
||||
|
||||
equal(owner:GetModelScale(), 0.5, "after :SetSize")
|
||||
root:Remove()
|
||||
equal(owner:GetModelScale(), 1, "should revert after root is removed")
|
||||
end
|
||||
|
||||
check_shared(owner, "GetCurrentViewOffset", Vector(0,0,64))
|
||||
|
||||
RunConsoleCommand("pac_modifier_size", "1")
|
||||
repeat yield() until GetConVar("pac_modifier_size"):GetBool()
|
||||
|
||||
pac.emut.MutateEntity(owner, "size", owner, 0.5)
|
||||
repeat yield() until owner:GetModelScale() == 0.5
|
||||
|
||||
check_shared(owner, "GetCurrentViewOffset", Vector(0,0,32))
|
||||
|
||||
equal(test.RunLuaOnServer("return Entity(" .. owner:EntIndex() .. "):GetModelScale()"), 0.5, " server mismatch with client")
|
||||
|
||||
pac.emut.MutateEntity(owner, "size", owner, 1)
|
||||
|
||||
repeat yield() until owner:GetModelScale() == 1
|
||||
check_shared(owner, "GetCurrentViewOffset", Vector(0,0,64))
|
||||
|
||||
RunConsoleCommand("pac_modifier_size", "0")
|
||||
repeat yield() until not GetConVar("pac_modifier_size"):GetBool()
|
||||
|
||||
pac.emut.MutateEntity(owner, "size", owner, 2)
|
||||
|
||||
repeat yield() until owner:GetModelScale() == 1
|
||||
check_shared(owner, "GetCurrentViewOffset", Vector(0,0,64))
|
||||
|
||||
pac.emut.RestoreMutations(owner, "size", owner)
|
||||
|
||||
RunConsoleCommand("pac_modifier_size", "1")
|
||||
repeat yield() until GetConVar("pac_modifier_size"):GetBool()
|
||||
|
||||
repeat yield() until owner:GetModelScale() == 1
|
||||
|
||||
check_shared(owner, "GetCurrentViewOffset", Vector(0,0,64))
|
||||
check_shared(owner, "GetViewOffsetDucked", Vector(0,0,28))
|
||||
check_shared(owner, "GetStepSize", 18)
|
||||
|
||||
done()
|
||||
end
|
||||
46
lua/pac3/core/client/tests/smoke.lua
Normal file
46
lua/pac3/core/client/tests/smoke.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
function test.Run(done)
|
||||
local function find_part_in_entities(part)
|
||||
for k,v in pairs(ents.GetAll()) do
|
||||
if v.PACPart == part then
|
||||
return v.PACPart
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, part in pairs(pac.GetLocalParts()) do
|
||||
part:Remove()
|
||||
end
|
||||
|
||||
assert(table.Count(pac.GetLocalParts()) == 0)
|
||||
|
||||
local part = pac.CreatePart("group")
|
||||
local model = part:CreatePart("model")
|
||||
assert(table.Count(pac.GetLocalParts()) == 2)
|
||||
|
||||
model:SetModel("models/props_combine/breenchair.mdl")
|
||||
|
||||
assert(find_part_in_entities(model) == model)
|
||||
|
||||
assert(model:GetOwner():GetModel() == model:GetOwner():GetModel())
|
||||
|
||||
model:Remove()
|
||||
|
||||
assert(table.Count(pac.GetLocalParts()) == 1)
|
||||
|
||||
part:Remove()
|
||||
|
||||
assert(table.Count(pac.GetLocalParts()) == 0)
|
||||
|
||||
done()
|
||||
end
|
||||
587
lua/pac3/core/client/util.lua
Normal file
587
lua/pac3/core/client/util.lua
Normal file
@@ -0,0 +1,587 @@
|
||||
--[[
|
||||
| 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
|
||||
69
lua/pac3/core/server/effects.lua
Normal file
69
lua/pac3/core/server/effects.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pac.EffectsBlackList =
|
||||
{
|
||||
"frozen_steam",
|
||||
"portal_rift_01",
|
||||
"explosion_silo",
|
||||
"citadel_shockwave_06",
|
||||
"citadel_shockwave",
|
||||
"choreo_launch_rocket_start",
|
||||
"choreo_launch_rocket_jet",
|
||||
}
|
||||
|
||||
if not pac_loaded_particle_effects then
|
||||
pac_loaded_particle_effects = {}
|
||||
local files = file.Find("particles/*.pcf", "GAME")
|
||||
|
||||
for key, file_name in pairs(files) do
|
||||
if not pac_loaded_particle_effects[file_name] and not pac.BlacklistedParticleSystems[file_name:lower()] then
|
||||
game.AddParticles("particles/" .. file_name)
|
||||
end
|
||||
|
||||
pac_loaded_particle_effects[file_name] = true
|
||||
end
|
||||
|
||||
pac.Message('Loaded total ', #files, ' particle systems')
|
||||
end
|
||||
|
||||
util.AddNetworkString("pac_effect_precached")
|
||||
util.AddNetworkString("pac_request_precache")
|
||||
|
||||
function pac.PrecacheEffect(name)
|
||||
PrecacheParticleSystem(name)
|
||||
net.Start("pac_effect_precached")
|
||||
net.WriteString(name)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
local queue = {}
|
||||
net.Receive("pac_request_precache", function(len, pl)
|
||||
local name = net.ReadString()
|
||||
if table.HasValue(pac.EffectsBlackList, name) then return end
|
||||
|
||||
-- Each player gets a 50 length queue
|
||||
local plqueue = queue[pl]
|
||||
if plqueue then
|
||||
if #plqueue<50 then plqueue[#plqueue+1] = name end
|
||||
else
|
||||
plqueue = {name}
|
||||
queue[pl] = plqueue
|
||||
local function processQueue()
|
||||
if #plqueue == 0 then
|
||||
queue[pl] = nil
|
||||
else
|
||||
timer.Simple(0.5, processQueue)
|
||||
pac.PrecacheEffect(table.remove(plqueue,1))
|
||||
end
|
||||
end
|
||||
processQueue()
|
||||
end
|
||||
end)
|
||||
87
lua/pac3/core/server/event.lua
Normal file
87
lua/pac3/core/server/event.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
util.AddNetworkString("pac_proxy")
|
||||
util.AddNetworkString("pac_event")
|
||||
|
||||
-- event
|
||||
concommand.Add("pac_event", function(ply, _, args)
|
||||
if not args[1] then return end
|
||||
|
||||
local event = args[1]
|
||||
local extra = tonumber(args[2]) or 0
|
||||
|
||||
if extra == 2 or args[2] == "toggle" then
|
||||
ply.pac_event_toggles = ply.pac_event_toggles or {}
|
||||
ply.pac_event_toggles[event] = not ply.pac_event_toggles[event]
|
||||
|
||||
extra = ply.pac_event_toggles[event] and 1 or 0
|
||||
end
|
||||
|
||||
net.Start("pac_event", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(event)
|
||||
net.WriteInt(extra, 8)
|
||||
net.Broadcast()
|
||||
end)
|
||||
|
||||
concommand.Add("+pac_event", function(ply, _, args)
|
||||
if not args[1] then return end
|
||||
|
||||
if args[2] == "2" or args[2] == "toggle" then
|
||||
local event = args[1]
|
||||
ply.pac_event_toggles = ply.pac_event_toggles or {}
|
||||
ply.pac_event_toggles[event] = true
|
||||
|
||||
net.Start("pac_event", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(event)
|
||||
net.WriteInt(1, 8)
|
||||
net.Broadcast()
|
||||
else
|
||||
net.Start("pac_event", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(args[1] .. "_on")
|
||||
net.Broadcast()
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("-pac_event", function(ply, _, args)
|
||||
if not args[1] then return end
|
||||
|
||||
if args[2] == "2" or args[2] == "toggle" then
|
||||
local event = args[1]
|
||||
ply.pac_event_toggles = ply.pac_event_toggles or {}
|
||||
ply.pac_event_toggles[event] = false
|
||||
net.Start("pac_event", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(event)
|
||||
net.WriteInt(0, 8)
|
||||
net.Broadcast()
|
||||
else
|
||||
net.Start("pac_event", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(args[1] .. "_off")
|
||||
net.Broadcast()
|
||||
end
|
||||
end)
|
||||
|
||||
-- proxy
|
||||
concommand.Add("pac_proxy", function(ply, _, args)
|
||||
net.Start("pac_proxy", true)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(args[1])
|
||||
|
||||
net.WriteFloat(tonumber(args[2]) or 0)
|
||||
net.WriteFloat(tonumber(args[3]) or 0)
|
||||
net.WriteFloat(tonumber(args[4]) or 0)
|
||||
net.Broadcast()
|
||||
end)
|
||||
38
lua/pac3/core/server/in_skybox.lua
Normal file
38
lua/pac3/core/server/in_skybox.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
hook.Add("InitPostEntity","pac_get_sky_camera",function()
|
||||
local sky_camera = ents.FindByClass("sky_camera")[1]
|
||||
if sky_camera then
|
||||
local nwVarName = "pac_in_skybox"
|
||||
local in_skybox = {}
|
||||
|
||||
timer.Create("pac_in_skybox", 0.5, 0, function()
|
||||
if not IsValid(sky_camera) then
|
||||
sky_camera = ents.FindByClass("sky_camera")[1]
|
||||
end
|
||||
local new_in_skybox = {}
|
||||
for _, ent in ipairs(ents.FindInPVS(sky_camera:GetPos())) do
|
||||
if not in_skybox[ent] then
|
||||
ent:SetNW2Bool(nwVarName, true)
|
||||
end
|
||||
new_in_skybox[ent] = true
|
||||
end
|
||||
|
||||
for ent in pairs(in_skybox) do
|
||||
if not new_in_skybox[ent] and ent:IsValid() then
|
||||
ent:SetNW2Bool(nwVarName, false)
|
||||
end
|
||||
end
|
||||
|
||||
in_skybox = new_in_skybox
|
||||
end)
|
||||
end
|
||||
end)
|
||||
33
lua/pac3/core/server/init.lua
Normal file
33
lua/pac3/core/server/init.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
pac = pac or {}
|
||||
|
||||
pac.Parts = pac.Parts or {}
|
||||
pac.Errors = {}
|
||||
pac.resource = include("pac3/libraries/resource.lua")
|
||||
|
||||
CreateConVar("has_pac3", "1", {FCVAR_NOTIFY})
|
||||
CreateConVar("pac_allow_blood_color", "1", {FCVAR_NOTIFY}, "Allow to use custom blood color")
|
||||
CreateConVar('pac_allow_mdl', '1', CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Allow to use custom MDLs')
|
||||
CreateConVar('pac_allow_mdl_entity', '1', CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, 'Allow to use custom MDLs as Entity')
|
||||
|
||||
include("util.lua")
|
||||
|
||||
include("pac3/core/shared/init.lua")
|
||||
|
||||
include("effects.lua")
|
||||
include("event.lua")
|
||||
include("net_messages.lua")
|
||||
include("test_suite_backdoor.lua")
|
||||
include("in_skybox.lua")
|
||||
|
||||
hook.Run("pac_Initialized")
|
||||
41
lua/pac3/core/server/net_messages.lua
Normal file
41
lua/pac3/core/server/net_messages.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
util.AddNetworkString("pac.AllowPlayerButtons")
|
||||
util.AddNetworkString("pac.BroadcastPlayerButton")
|
||||
|
||||
do -- button event
|
||||
net.Receive("pac.AllowPlayerButtons", function(length, client)
|
||||
local key = net.ReadUInt(8)
|
||||
|
||||
client.pac_broadcast_buttons = client.pac_broadcast_buttons or {}
|
||||
client.pac_broadcast_buttons[key] = true
|
||||
end)
|
||||
|
||||
local function broadcast_key(ply, key, down)
|
||||
if not ply.pac_broadcast_buttons then return end
|
||||
if not ply.pac_broadcast_buttons[key] then return end
|
||||
|
||||
net.Start("pac.BroadcastPlayerButton")
|
||||
net.WriteEntity(ply)
|
||||
net.WriteUInt(key, 8)
|
||||
net.WriteBool(down)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
pac.AddHook("PlayerButtonDown", "event", function(ply, key)
|
||||
broadcast_key(ply, key, true)
|
||||
end)
|
||||
|
||||
pac.AddHook("PlayerButtonUp", "event", function(ply, key)
|
||||
broadcast_key(ply, key, false)
|
||||
end)
|
||||
end
|
||||
37
lua/pac3/core/server/test_suite_backdoor.lua
Normal file
37
lua/pac3/core/server/test_suite_backdoor.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- this is for the test suite, it is technically a lua run backdoor
|
||||
-- the test suite is a utility for the pac developers to ensure that pac works and don't break
|
||||
-- after making changes to it
|
||||
|
||||
util.AddNetworkString("pac3_test_suite_backdoor_receive_results")
|
||||
util.AddNetworkString("pac3_test_suite_backdoor")
|
||||
|
||||
net.Receive("pac3_test_suite_backdoor", function(len, ply)
|
||||
-- needs to be enabled through lua first, eg lua_run pac.test_suite_backdoor_enabled = true
|
||||
if not pac.test_suite_backdoor_enabled then return end
|
||||
|
||||
-- need to be at least super admin
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
|
||||
local id = net.ReadString()
|
||||
local lua_code = net.ReadString()
|
||||
|
||||
|
||||
local func = CompileString(lua_code, "pac3_test_suite_backdoor")
|
||||
|
||||
local res = {func()}
|
||||
|
||||
net.Start("pac3_test_suite_backdoor_receive_results")
|
||||
net.WriteString(id)
|
||||
net.WriteTable(res)
|
||||
net.Send(ply)
|
||||
end)
|
||||
109
lua/pac3/core/server/util.lua
Normal file
109
lua/pac3/core/server/util.lua
Normal file
@@ -0,0 +1,109 @@
|
||||
--[[
|
||||
| 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 CurTime = CurTime
|
||||
local math_Clamp = math.Clamp
|
||||
|
||||
function pac.dprint(fmt, ...)
|
||||
if pac.debug then
|
||||
MsgN("\n")
|
||||
MsgN(">>>PAC3>>>")
|
||||
MsgN(fmt:format(...))
|
||||
if pac.debug_trace then
|
||||
MsgN("==TRACE==")
|
||||
debug.Trace()
|
||||
MsgN("==TRACE==")
|
||||
end
|
||||
MsgN("<<<PAC3<<<")
|
||||
MsgN("\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function pac.CallHook(str, ...)
|
||||
return hook.Call("pac_" .. str, GAMEMODE, ...)
|
||||
end
|
||||
|
||||
function pac.AddHook(str, id, func, priority)
|
||||
id = "pac_" .. id
|
||||
|
||||
if not DLib and not ULib then
|
||||
priority = nil
|
||||
end
|
||||
|
||||
hook.Add(str, id, function(...)
|
||||
local status, a, b, c, d, e, f, g = pcall(func, ...)
|
||||
|
||||
if not status then
|
||||
pac.Message('Error on hook ' .. str .. ' (' .. id .. ')! ', a)
|
||||
return
|
||||
end
|
||||
|
||||
return a, b, c, d, e, f, g
|
||||
end, priority)
|
||||
end
|
||||
|
||||
function pac.RemoveHook(str, id)
|
||||
id = "pac_" .. id
|
||||
|
||||
hook.Remove(str, id)
|
||||
end
|
||||
|
||||
function pac.RatelimitAlert( ply, id, message )
|
||||
if not ply.pac_ratelimit_alerts then
|
||||
ply.pac_ratelimit_alerts = {}
|
||||
end
|
||||
|
||||
if CurTime() >= ( ply.pac_ratelimit_alerts[id] or 0 ) then
|
||||
ply.pac_ratelimit_alerts[id] = CurTime() + 3
|
||||
if isstring(message) then
|
||||
pac.Message(message)
|
||||
end
|
||||
if istable(message) then
|
||||
pac.Message(unpack(message))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local RatelimitAlert = pac.RatelimitAlert
|
||||
|
||||
function pac.RatelimitPlayer( ply, name, buffer, refill, message )
|
||||
local ratelimitName = "pac_ratelimit_" .. name
|
||||
local checkName = "pac_ratelimit_check_" .. name
|
||||
|
||||
if not ply[ratelimitName] then ply[ratelimitName] = buffer end
|
||||
|
||||
local curTime = CurTime()
|
||||
if not ply[checkName] then ply[checkName] = curTime end
|
||||
|
||||
local dripSize = curTime - ply[checkName]
|
||||
ply[checkName] = curTime
|
||||
|
||||
local drip = dripSize / refill
|
||||
local newVal = ply[ratelimitName] + drip
|
||||
|
||||
ply[ratelimitName] = math_Clamp(newVal, 0, buffer)
|
||||
|
||||
if ply[ratelimitName] >= 1 then
|
||||
ply[ratelimitName] = ply[ratelimitName] - 1
|
||||
return true
|
||||
else
|
||||
if message then
|
||||
RatelimitAlert(ply, name, message)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function pac.GetRateLimitPlayerBuffer( ply, name )
|
||||
local ratelimitName = "pac_ratelimit_" .. name
|
||||
return ply[ratelimitName] or 0
|
||||
end
|
||||
|
||||
377
lua/pac3/core/shared/entity_mutator.lua
Normal file
377
lua/pac3/core/shared/entity_mutator.lua
Normal file
@@ -0,0 +1,377 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- rate limit?
|
||||
|
||||
local CLIENT = CLIENT
|
||||
local SERVER = SERVER
|
||||
|
||||
if pac.emut then
|
||||
for _, ent in ipairs(ents.GetAll()) do
|
||||
if ent.pac_mutations then
|
||||
for _, mutator in pairs(ent.pac_mutations) do
|
||||
xpcall(pac.emut.RestoreMutations, function() end, mutator.Owner, mutator.ClassName, mutator.Entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local emut = {}
|
||||
|
||||
pac.emut = emut
|
||||
|
||||
emut.registered_mutators = {}
|
||||
|
||||
do
|
||||
-- TOOD: use list instead of hash map
|
||||
emut.active_mutators = {}
|
||||
|
||||
function emut.AddMutator(mutator)
|
||||
emut.active_mutators[mutator] = mutator
|
||||
end
|
||||
|
||||
function emut.RemoveMutator(mutator)
|
||||
emut.active_mutators[mutator] = nil
|
||||
end
|
||||
|
||||
function emut.RemoveMutatorsOwnedByEntity(ent)
|
||||
if not ent.pac_mutations then return end
|
||||
|
||||
for class_name, mutator in pairs(ent.pac_mutations) do
|
||||
emut.RemoveMutator(mutator)
|
||||
ent.pac_mutations[class_name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function emut.GetAllMutators()
|
||||
local out = {}
|
||||
|
||||
for _, mutator in pairs(emut.active_mutators) do
|
||||
table.insert(out, mutator)
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
end
|
||||
|
||||
local function on_error(msg)
|
||||
print(debug.traceback(msg))
|
||||
ErrorNoHalt(msg)
|
||||
end
|
||||
|
||||
local suppress_send_to_server = false
|
||||
local override_enabled = false
|
||||
|
||||
function emut.MutateEntity(owner, class_name, ent, ...)
|
||||
if not IsValid(owner) then owner = game.GetWorld() end
|
||||
assert(emut.registered_mutators[class_name], "invalid mutator " .. class_name)
|
||||
if not IsValid(ent) then ErrorNoHalt("entity is invalid") return end
|
||||
|
||||
if hook.Run("PACMutateEntity", owner, ent, class_name, ...) == false then
|
||||
return
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
if pace.IsBanned(owner) then return end
|
||||
|
||||
if override_enabled and owner:IsPlayer() and not emut.registered_mutators[class_name].cvar:GetBool() then
|
||||
pac.Message(owner, "tried to set size when it's disabled")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
ent.pac_mutations = ent.pac_mutations or {}
|
||||
|
||||
local mutator = ent.pac_mutations[class_name]
|
||||
|
||||
if not mutator then
|
||||
mutator = setmetatable({Entity = ent, Owner = owner}, emut.registered_mutators[class_name])
|
||||
mutator.original_state = {mutator:StoreState()}
|
||||
ent.pac_mutations[class_name] = mutator
|
||||
emut.AddMutator(mutator)
|
||||
end
|
||||
|
||||
-- notify about owner change?
|
||||
mutator.Owner = owner
|
||||
mutator.current_state = {...}
|
||||
|
||||
if not xpcall(mutator.Mutate, on_error, mutator, ...) then
|
||||
mutator.current_state = nil
|
||||
emut.RestoreMutations(owner, class_name, ent)
|
||||
return
|
||||
end
|
||||
|
||||
if CLIENT and not emut.registered_mutators[class_name].cvar:GetBool() then
|
||||
return false
|
||||
end
|
||||
|
||||
if CLIENT and owner == LocalPlayer() and not suppress_send_to_server then
|
||||
net.Start("pac_entity_mutator")
|
||||
net.WriteString(class_name)
|
||||
net.WriteEntity(ent)
|
||||
net.WriteBool(false)
|
||||
mutator:WriteArguments(...)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
net.Start("pac_entity_mutator")
|
||||
net.WriteEntity(owner)
|
||||
net.WriteString(class_name)
|
||||
net.WriteEntity(ent)
|
||||
net.WriteBool(false)
|
||||
mutator:WriteArguments(...)
|
||||
net.SendPVS(owner:GetPos())
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function emut.RestoreMutations(owner, class_name, ent)
|
||||
if not IsValid(owner) then owner = game.GetWorld() end
|
||||
assert(emut.registered_mutators[class_name], "invalid mutator " .. class_name)
|
||||
if not IsValid(ent) then ErrorNoHalt("entity is invalid") return end
|
||||
|
||||
if SERVER then
|
||||
if not override_enabled then
|
||||
if not emut.registered_mutators[class_name].cvar:GetBool() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local mutator = ent.pac_mutations and ent.pac_mutations[class_name]
|
||||
|
||||
if mutator then
|
||||
xpcall(mutator.Mutate, on_error, mutator, unpack(mutator.original_state))
|
||||
ent.pac_mutations[class_name] = nil
|
||||
emut.RemoveMutator(mutator)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
if not emut.registered_mutators[class_name].cvar:GetBool() then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT and owner == LocalPlayer() and not suppress_send_to_server then
|
||||
net.Start("pac_entity_mutator")
|
||||
net.WriteString(class_name)
|
||||
net.WriteEntity(ent)
|
||||
net.WriteBool(true)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
net.Start("pac_entity_mutator")
|
||||
net.WriteEntity(owner)
|
||||
net.WriteString(class_name)
|
||||
net.WriteEntity(ent)
|
||||
net.WriteBool(true)
|
||||
net.SendPVS(owner:GetPos())
|
||||
-- we also include the player who made the mutations, in case the server wants the arguments to be something else
|
||||
end
|
||||
end
|
||||
|
||||
function emut.Register(meta)
|
||||
|
||||
if Entity(1):IsValid() then
|
||||
for _, ent in ipairs(ents.GetAll()) do
|
||||
if ent.pac_mutations then
|
||||
for class_name, mutator in pairs(ent.pac_mutations) do
|
||||
if class_name == meta.ClassName then
|
||||
xpcall(emut.RestoreMutations, function() end, mutator.Owner, mutator.ClassName, mutator.Entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
meta.Mutate = meta.Mutate or function() end
|
||||
meta.StoreState = meta.StoreState or function() end
|
||||
|
||||
function meta:Disable()
|
||||
if self.disabled_state then return end
|
||||
|
||||
local state = {xpcall(self.StoreState, on_error, self)}
|
||||
if state[1] then
|
||||
table.remove(state, 1)
|
||||
self.disabled_state = state
|
||||
override_enabled = true
|
||||
xpcall(emut.MutateEntity, on_error, self.Owner, self.ClassName, self.Entity, unpack(self.original_state))
|
||||
override_enabled = false
|
||||
end
|
||||
end
|
||||
|
||||
function meta:Enable()
|
||||
if not self.disabled_state then return end
|
||||
|
||||
xpcall(emut.MutateEntity, on_error, self.Owner, self.ClassName, self.Entity, unpack(self.disabled_state))
|
||||
|
||||
self.disabled_state = nil
|
||||
end
|
||||
|
||||
function meta:__tostring()
|
||||
return "mutator[" .. self.ClassName .. "]" .. "[" .. tostring(self.Owner) .. "]" .. "[" .. tostring(self.Entity) .. "]"
|
||||
end
|
||||
|
||||
meta.__index = meta
|
||||
emut.registered_mutators[meta.ClassName] = meta
|
||||
|
||||
do
|
||||
local name = "pac_modifier_" .. meta.ClassName
|
||||
|
||||
local default = 1
|
||||
|
||||
if GAMEMODE and GAMEMODE.FolderName and not GAMEMODE.FolderName:lower():find("sandbox") then
|
||||
default = 0
|
||||
end
|
||||
|
||||
meta.cvar = CreateConVar(name, default, CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||
|
||||
if SERVER then
|
||||
cvars.AddChangeCallback(name, function()
|
||||
local enable = meta.cvar:GetBool()
|
||||
|
||||
if enable then
|
||||
pac.Message("entity modifier ", name, " is now enabled")
|
||||
emut.EnableMutator()
|
||||
else
|
||||
pac.Message("entity modifier ", name, " is now disabled")
|
||||
emut.DisableMutator()
|
||||
end
|
||||
|
||||
end, name .. "_change")
|
||||
end
|
||||
end
|
||||
|
||||
if meta.Update then
|
||||
timer.Create("pac_entity_mutator_" .. meta.ClassName, meta.UpdateRate, 0, function()
|
||||
|
||||
if not meta.cvar:GetBool() then return end
|
||||
|
||||
for _, mutator in ipairs(emut.GetAllMutators()) do
|
||||
if mutator.ClassName == meta.ClassName then
|
||||
if not xpcall(mutator.Update, on_error, mutator, unpack(mutator.current_state)) then
|
||||
xpcall(emut.RestoreMutations, on_error, mutator.Owner, mutator.ClassName, mutator.Entity)
|
||||
emut.RemoveMutator(mutator)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("pac_entity_mutator")
|
||||
net.Receive("pac_entity_mutator", function(len, ply)
|
||||
local class_name = net.ReadString()
|
||||
if not emut.registered_mutators[class_name] then return end
|
||||
|
||||
local ent = net.ReadEntity()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
if net.ReadBool() then
|
||||
emut.RestoreMutations(ply, class_name, ent)
|
||||
else
|
||||
if not pace.CanPlayerModify(ply, ent) then return end
|
||||
|
||||
emut.MutateEntity(ply, class_name, ent, emut.registered_mutators[class_name].ReadArguments())
|
||||
end
|
||||
end)
|
||||
|
||||
function emut.EnableMutator()
|
||||
for _, mutator in ipairs(emut.GetAllMutators()) do
|
||||
mutator:Enable()
|
||||
end
|
||||
end
|
||||
|
||||
function emut.DisableMutator()
|
||||
for _, mutator in ipairs(emut.GetAllMutators()) do
|
||||
mutator:Disable()
|
||||
end
|
||||
end
|
||||
|
||||
function emut.ReplicateMutatorsForPlayer(ply)
|
||||
for _, mutator in ipairs(emut.GetAllMutators()) do
|
||||
net.Start("pac_entity_mutator")
|
||||
net.WriteEntity(mutator.Owner)
|
||||
net.WriteString(mutator.ClassName)
|
||||
net.WriteEntity(mutator.Entity)
|
||||
net.WriteBool(false)
|
||||
mutator:WriteArguments(unpack(mutator.current_state))
|
||||
net.Send(ply)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "pac_entity_mutators_spawn", function(ply)
|
||||
local id = "pac_entity_mutators_spawn" .. ply:UniqueID()
|
||||
hook.Add( "SetupMove", id, function(movingPly, _, cmd)
|
||||
if not ply:IsValid() then
|
||||
hook.Remove("SetupMove", id)
|
||||
elseif movingPly == ply and not cmd:IsForced() then
|
||||
emut.ReplicateMutatorsForPlayer(ply)
|
||||
|
||||
hook.Remove("SetupMove", id)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function emut.RemoveMutationsForPlayer(ply)
|
||||
for _, mutator in ipairs(emut.GetAllMutators()) do
|
||||
if mutator.Owner == ply then
|
||||
emut.RestoreMutations(mutator.Owner, mutator.ClassName, mutator.Entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("EntityRemoved", "pac_entity_mutators_left", function(ent)
|
||||
if not IsValid(ent) then return end
|
||||
if ent:IsPlayer() then
|
||||
if Player(ent:UserID()) == NULL then
|
||||
emut.RemoveMutationsForPlayer(ent)
|
||||
end
|
||||
else
|
||||
emut.RemoveMutatorsOwnedByEntity(ent)
|
||||
end
|
||||
end)
|
||||
|
||||
if CLIENT then
|
||||
net.Receive("pac_entity_mutator", function(len)
|
||||
local ply = net.ReadEntity()
|
||||
if not ply:IsValid() then return end
|
||||
local class_name = net.ReadString()
|
||||
local ent = net.ReadEntity()
|
||||
if not ent:IsValid() then return end
|
||||
|
||||
suppress_send_to_server = true
|
||||
|
||||
xpcall(function()
|
||||
if net.ReadBool() then
|
||||
emut.RestoreMutations(ply, class_name, ent)
|
||||
else
|
||||
emut.MutateEntity(ply, class_name, ent, emut.registered_mutators[class_name].ReadArguments())
|
||||
end
|
||||
end, on_error)
|
||||
|
||||
suppress_send_to_server = false
|
||||
end)
|
||||
end
|
||||
|
||||
function emut.LoadMutators()
|
||||
local files = file.Find("pac3/core/shared/entity_mutators/*.lua", "LUA")
|
||||
|
||||
for _, name in pairs(files) do
|
||||
include("pac3/core/shared/entity_mutators/" .. name)
|
||||
end
|
||||
end
|
||||
|
||||
emut.LoadMutators()
|
||||
34
lua/pac3/core/shared/entity_mutators/blood_color.lua
Normal file
34
lua/pac3/core/shared/entity_mutators/blood_color.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
--[[
|
||||
| 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 MUTATOR = {}
|
||||
|
||||
MUTATOR.ClassName = "blood_color"
|
||||
|
||||
function MUTATOR:WriteArguments(enum)
|
||||
assert(enum >= -1 and enum <= 6, "invalid blood color")
|
||||
net.WriteInt(enum, 8)
|
||||
end
|
||||
|
||||
function MUTATOR:ReadArguments()
|
||||
return net.ReadInt(8)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function MUTATOR:StoreState()
|
||||
return self.Entity:GetBloodColor()
|
||||
end
|
||||
|
||||
function MUTATOR:Mutate(enum)
|
||||
self.Entity:SetBloodColor(enum)
|
||||
end
|
||||
end
|
||||
|
||||
pac.emut.Register(MUTATOR)
|
||||
81
lua/pac3/core/shared/entity_mutators/model.lua
Normal file
81
lua/pac3/core/shared/entity_mutators/model.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
--[[
|
||||
| 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 MUTATOR = {}
|
||||
|
||||
MUTATOR.ClassName = "model"
|
||||
MUTATOR.UpdateRate = 0.25
|
||||
|
||||
function MUTATOR:WriteArguments(path)
|
||||
assert(isstring(path), "path must be a string")
|
||||
|
||||
net.WriteString(path)
|
||||
end
|
||||
|
||||
function MUTATOR:ReadArguments()
|
||||
return net.ReadString()
|
||||
end
|
||||
|
||||
function MUTATOR:Update(val)
|
||||
if not self.actual_model or not IsValid(self.Entity) then return end
|
||||
|
||||
if self.Entity:GetModel():lower() ~= self.actual_model:lower() then
|
||||
self.Entity:SetModel(self.actual_model)
|
||||
end
|
||||
end
|
||||
|
||||
function MUTATOR:StoreState()
|
||||
return self.Entity:GetModel()
|
||||
end
|
||||
|
||||
function MUTATOR:Mutate(path)
|
||||
if path:find("^http") then
|
||||
if SERVER and pac.debug then
|
||||
if self.Owner:IsPlayer() then
|
||||
pac.Message(self.Owner, " wants to use ", path, " as model on ", ent)
|
||||
end
|
||||
end
|
||||
|
||||
local ent_str = tostring(self.Entity)
|
||||
|
||||
pac.DownloadMDL(path, function(mdl_path)
|
||||
if not self.Entity:IsValid() then
|
||||
pac.Message("cannot set model ", mdl_path, " on ", ent_str ,': entity became invalid')
|
||||
return
|
||||
end
|
||||
|
||||
if SERVER and pac.debug then
|
||||
pac.Message(mdl_path, " downloaded for ", ent, ': ', path)
|
||||
end
|
||||
|
||||
self.Entity:SetModel(mdl_path)
|
||||
self.actual_model = mdl_path
|
||||
|
||||
end, function(err)
|
||||
pac.Message(err)
|
||||
end, self.Owner)
|
||||
else
|
||||
if path:EndsWith(".mdl") then
|
||||
self.Entity:SetModel(path)
|
||||
|
||||
if self.Owner:IsPlayer() and path:lower() ~= self.Entity:GetModel():lower() then
|
||||
self.Owner:ChatPrint('[PAC3] ERROR: ' .. path .. " is not a valid model on the server.")
|
||||
else
|
||||
self.actual_model = path
|
||||
end
|
||||
else
|
||||
local translated = player_manager.TranslatePlayerModel(path)
|
||||
self.Entity:SetModel(translated)
|
||||
self.actual_model = translated
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pac.emut.Register(MUTATOR)
|
||||
131
lua/pac3/core/shared/entity_mutators/size.lua
Normal file
131
lua/pac3/core/shared/entity_mutators/size.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
--[[
|
||||
| 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 MUTATOR = {}
|
||||
|
||||
MUTATOR.ClassName = "size"
|
||||
|
||||
function MUTATOR:WriteArguments(multiplier, other)
|
||||
net.WriteFloat(multiplier)
|
||||
if other then
|
||||
net.WriteBool(true)
|
||||
net.WriteFloat(other.StandingHullHeight)
|
||||
net.WriteFloat(other.CrouchingHullHeight)
|
||||
net.WriteFloat(other.HullWidth)
|
||||
else
|
||||
net.WriteBool(false)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local hidden_state = self.original_state[3]
|
||||
if hidden_state then
|
||||
net.WriteBool(true)
|
||||
net.WriteTable(hidden_state)
|
||||
else
|
||||
net.WriteBool(false)
|
||||
end
|
||||
else
|
||||
net.WriteBool(false)
|
||||
end
|
||||
end
|
||||
|
||||
function MUTATOR:ReadArguments()
|
||||
local multiplier = math.Clamp(net.ReadFloat(), 0.1, 10)
|
||||
local other = false
|
||||
local hidden_state
|
||||
|
||||
if net.ReadBool() then
|
||||
other = {}
|
||||
other.StandingHullHeight = net.ReadFloat()
|
||||
other.CrouchingHullHeight = net.ReadFloat()
|
||||
other.HullWidth = net.ReadFloat()
|
||||
end
|
||||
|
||||
if net.ReadBool() then
|
||||
hidden_state = net.ReadTable()
|
||||
end
|
||||
|
||||
return multiplier, other, hidden_state
|
||||
end
|
||||
|
||||
function MUTATOR:StoreState()
|
||||
local ent = self.Entity
|
||||
|
||||
return
|
||||
1, --ent:GetModelScale(),
|
||||
false, -- we will just ent:ResetHull()
|
||||
{
|
||||
ViewOffset = ent.GetViewOffset and ent:GetViewOffset() or nil,
|
||||
ViewOffsetDucked = ent.GetViewOffsetDucked and ent:GetViewOffsetDucked() or nil,
|
||||
StepSize = ent.GetStepSize and ent:GetStepSize() or nil,
|
||||
}
|
||||
end
|
||||
|
||||
local functions = {
|
||||
"ViewOffset",
|
||||
"ViewOffsetDucked",
|
||||
"StepSize",
|
||||
}
|
||||
|
||||
function MUTATOR:Mutate(multiplier, other, hidden_state)
|
||||
local ent = self.Entity
|
||||
|
||||
if ent:GetModelScale() ~= multiplier then
|
||||
ent:SetModelScale(multiplier)
|
||||
end
|
||||
|
||||
-- hmmm
|
||||
hidden_state = hidden_state or self.original_state[3]
|
||||
|
||||
if hidden_state then
|
||||
for _, key in ipairs(functions) do
|
||||
local original = hidden_state[key]
|
||||
if original then
|
||||
local setter = ent["Set" .. key]
|
||||
|
||||
if setter then
|
||||
setter(ent, original * multiplier)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ent.SetHull and ent.SetHullDuck and ent.ResetHull then
|
||||
if other then
|
||||
local smin, smax = Vector(), Vector()
|
||||
local cmin, cmax = Vector(), Vector()
|
||||
|
||||
local w = math.Clamp(other.HullWidth or 32, 1, 4096)
|
||||
|
||||
smin.x = -w / 2
|
||||
smax.x = w / 2
|
||||
smin.y = -w / 2
|
||||
smax.y = w / 2
|
||||
|
||||
cmin.x = -w / 2
|
||||
cmax.x = w / 2
|
||||
cmin.y = -w / 2
|
||||
cmax.y = w / 2
|
||||
|
||||
smin.z = 0
|
||||
smax.z = math.Clamp(other.StandingHullHeight or 72, 1, 4096)
|
||||
|
||||
cmin.z = 0
|
||||
cmax.z = math.Clamp(other.CrouchingHullHeight or 36, 1, 4096)
|
||||
|
||||
ent:SetHull(smin, smax)
|
||||
ent:SetHullDuck(cmin, cmax)
|
||||
else
|
||||
ent:ResetHull()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pac.emut.Register(MUTATOR)
|
||||
64
lua/pac3/core/shared/footsteps_fix.lua
Normal file
64
lua/pac3/core/shared/footsteps_fix.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
if game.SinglePlayer() then
|
||||
if SERVER then
|
||||
util.AddNetworkString('pac_footstep')
|
||||
util.AddNetworkString('pac_footstep_request_state_update')
|
||||
util.AddNetworkString('pac_signal_mute_footstep')
|
||||
|
||||
hook.Add("PlayerFootstep", "footstep_fix", function(ply, pos, _, snd, vol)
|
||||
net.Start("pac_footstep_request_state_update")
|
||||
net.Send(ply)
|
||||
|
||||
net.Start("pac_footstep")
|
||||
net.WriteEntity(ply)
|
||||
net.WriteVector(pos)
|
||||
net.WriteString(snd)
|
||||
net.WriteFloat(vol)
|
||||
net.Broadcast()
|
||||
end)
|
||||
|
||||
net.Receive("pac_signal_mute_footstep", function(len,ply)
|
||||
local b = net.ReadBool()
|
||||
ply.pac_mute_footsteps = b
|
||||
if ply.pac_mute_footsteps then
|
||||
hook.Add("PlayerFootstep", "pac_footstep_silence", function()
|
||||
return b
|
||||
end)
|
||||
else hook.Remove("PlayerFootstep", "pac_footstep_silence") end
|
||||
end)
|
||||
|
||||
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
net.Receive("pac_footstep", function(len)
|
||||
local ply = net.ReadEntity()
|
||||
local pos = net.ReadVector()
|
||||
local snd = net.ReadString()
|
||||
local vol = net.ReadFloat()
|
||||
|
||||
if ply:IsValid() then
|
||||
hook.Run("pac_PlayerFootstep", ply, pos, snd, vol)
|
||||
end
|
||||
end)
|
||||
net.Receive("pac_footstep_request_state_update", function()
|
||||
net.Start("pac_signal_mute_footstep")
|
||||
net.WriteBool(LocalPlayer().pac_mute_footsteps)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
else
|
||||
hook.Add("PlayerFootstep", "footstep_fix", function(ply, pos, _, snd, vol)
|
||||
return hook.Run("pac_PlayerFootstep", ply, pos, snd, vol)
|
||||
end)
|
||||
end
|
||||
77
lua/pac3/core/shared/hash.lua
Normal file
77
lua/pac3/core/shared/hash.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
| 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 string_hash = util.SHA256
|
||||
local is_singleplayer = game.SinglePlayer()
|
||||
local is_bot = FindMetaTable("Player").IsBot
|
||||
local steamid64 = FindMetaTable("Player").SteamID64
|
||||
|
||||
function pac.Hash(obj)
|
||||
local t = type(obj)
|
||||
|
||||
if t == "string" then
|
||||
return string_hash(obj)
|
||||
elseif t == "Player" then
|
||||
if is_singleplayer then
|
||||
return "SinglePlayer"
|
||||
end
|
||||
|
||||
-- Player(s) can never be next bots (?)
|
||||
-- IsNextBot is present in Entity and NextBot metatables (and those functions are different)
|
||||
-- but not in Player's one
|
||||
if obj:IsNextBot() then
|
||||
return "nextbot " .. tostring(obj:EntIndex())
|
||||
end
|
||||
|
||||
if is_bot(obj) then
|
||||
return "bot " .. tostring(obj:EntIndex())
|
||||
end
|
||||
|
||||
local hash = steamid64(obj)
|
||||
if not hash then
|
||||
if pac.debug then
|
||||
ErrorNoHaltWithStack( "FIXME: Did not get a steamid64 for a player object " .. tostring(obj) .. ', valid=' .. tostring(IsValid(obj)) .. ', steamid=' .. tostring(obj:SteamID()) .. '\n' )
|
||||
end
|
||||
hash = "0"
|
||||
end
|
||||
return hash
|
||||
elseif t == "number" then
|
||||
return string_hash(tostring(t))
|
||||
elseif t == "table" then
|
||||
return string_hash(("%p"):format(obj))
|
||||
elseif t == "nil" then
|
||||
return string_hash(SysTime() .. ' ' .. os.time() .. ' ' .. RealTime())
|
||||
elseif IsEntity(obj) then
|
||||
return tostring(obj:EntIndex())
|
||||
else
|
||||
error("NYI " .. t)
|
||||
end
|
||||
end
|
||||
|
||||
function pac.ReverseHash(str, t)
|
||||
if t == "Player" then
|
||||
if is_singleplayer then
|
||||
return Entity(1)
|
||||
end
|
||||
|
||||
if str:StartWith("nextbot ") then
|
||||
return pac.ReverseHash(str:sub(#"nextbot " + 1), "Entity")
|
||||
elseif str:StartWith("bot ") then
|
||||
return pac.ReverseHash(str:sub(#"bot " + 1), "Entity")
|
||||
end
|
||||
|
||||
return player.GetBySteamID64(str) or NULL
|
||||
elseif t == "Entity" then
|
||||
return ents.GetByIndex(tonumber(str))
|
||||
else
|
||||
error("NYI " .. t)
|
||||
end
|
||||
end
|
||||
201
lua/pac3/core/shared/http.lua
Normal file
201
lua/pac3/core/shared/http.lua
Normal file
@@ -0,0 +1,201 @@
|
||||
--[[
|
||||
| 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 CL_LIMIT, CL_LIMIT_OVERRIDE, CL_NO_CLENGTH
|
||||
|
||||
if CLIENT then
|
||||
CL_LIMIT = CreateConVar("pac_webcontent_limit", "-1", {FCVAR_ARCHIVE}, "webcontent limit in kb, -1 = unlimited")
|
||||
CL_NO_CLENGTH = CreateConVar("pac_webcontent_allow_no_content_length", "0", {FCVAR_ARCHIVE}, "allow downloads with no content length")
|
||||
CL_LIMIT_OVERRIDE = CreateConVar("pac_webcontent_limit_force", "0", {FCVAR_ARCHIVE}, "Override serverside setting")
|
||||
end
|
||||
|
||||
local SV_LIMIT = CreateConVar("sv_pac_webcontent_limit", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "webcontent limit in kb, -1 = unlimited")
|
||||
local SV_NO_CLENGTH = CreateConVar("sv_pac_webcontent_allow_no_content_length", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "allow downloads with no content length")
|
||||
|
||||
function pac.FixGMODUrl(url)
|
||||
-- to avoid "invalid url" errors
|
||||
-- gmod does not allow urls containing "10.", "172.16.", "192.168.", "127." or "://localhost"
|
||||
-- we escape 10. and 127. can occur (mydomain.com/model10.zip) and assume the server supports
|
||||
-- the escaped request
|
||||
return url:Replace("10.", "%31%30%2e"):Replace("127.", "%31%32%37%2e")
|
||||
end
|
||||
|
||||
local function http(method, url, headers, cb, failcb)
|
||||
|
||||
url = pac.FixGMODUrl(url)
|
||||
|
||||
return HTTP({
|
||||
method = method,
|
||||
url = url,
|
||||
headers = headers,
|
||||
success = function(code, data, headers)
|
||||
if code < 400 then
|
||||
cb(data, #data, headers)
|
||||
else
|
||||
local header = {}
|
||||
for k,v in pairs(headers) do
|
||||
table.insert(header, tostring(k) .. ": " .. tostring(v))
|
||||
end
|
||||
|
||||
local err = "server returned code " .. code .. ":\n\n"
|
||||
err = err .. "url: "..url.."\n"
|
||||
err = err .. "================\n"
|
||||
|
||||
err = err .. "HEADER:\n"
|
||||
err = err .. table.concat(header, "\n") .. "\n"
|
||||
|
||||
err = err .. "================\n"
|
||||
|
||||
err = err .. "BODY:\n"
|
||||
err = err .. data .. "\n"
|
||||
|
||||
err = err .. "================\n"
|
||||
failcb(err, code >= 400, code)
|
||||
end
|
||||
end,
|
||||
failed = function(err)
|
||||
if failcb then
|
||||
failcb("_G.HTTP error: " .. err)
|
||||
else
|
||||
pac.Message("_G.HTTP error: " .. err)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function pac.FixUrl(url)
|
||||
url = url:Trim()
|
||||
url = url:gsub("[\"'<>\n\\]+", "")
|
||||
|
||||
if url:find("dropbox", 1, true) then
|
||||
url = url:gsub([[^http%://dl%.dropboxusercontent%.com/]], [[https://dl.dropboxusercontent.com/]])
|
||||
url = url:gsub([[^https?://dl.dropbox.com/]], [[https://www.dropbox.com/]])
|
||||
url = url:gsub([[^https?://www.dropbox.com/s/(.+)%?dl%=[01]$]], [[https://dl.dropboxusercontent.com/s/%1]])
|
||||
url = url:gsub([[^https?://www.dropbox.com/s/(.+)$]], [[https://dl.dropboxusercontent.com/s/%1]])
|
||||
url = url:gsub([[^https?://www.dropbox.com/scl/(.+)$]], [[https://dl.dropboxusercontent.com/scl/%1]]) --Fix for new dropbox format.
|
||||
return url
|
||||
end
|
||||
|
||||
if url:find("drive.google.com", 1, true) and not url:find("export=download", 1, true) then
|
||||
local id =
|
||||
url:match("https://drive.google.com/file/d/(.-)/") or
|
||||
url:match("https://drive.google.com/file/d/(.-)$") or
|
||||
url:match("https://drive.google.com/open%?id=(.-)$")
|
||||
|
||||
if id then
|
||||
return "https://drive.google.com/uc?export=download&id=" .. id
|
||||
end
|
||||
return url
|
||||
end
|
||||
|
||||
if url:find("gitlab.com", 1, true) then
|
||||
return url:gsub("^(https?://.-/.-/.-/)blob", "%1raw")
|
||||
end
|
||||
|
||||
url = url:gsub([[^http%://onedrive%.live%.com/redir?]],[[https://onedrive.live.com/download?]])
|
||||
url = url:gsub("pastebin.com/([a-zA-Z0-9]*)$", "pastebin.com/raw.php?i=%1")
|
||||
url = url:gsub("github.com/([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/blob/", "github.com/%1/%2/raw/")
|
||||
|
||||
return url
|
||||
end
|
||||
|
||||
function pac.getContentLength(url, cb, failcb)
|
||||
return http("HEAD", url, {["Accept-Encoding"] = "none"}, function(_, _, headers)
|
||||
local length
|
||||
|
||||
-- server have rights to send headers in any case
|
||||
for key, value in pairs(headers) do
|
||||
if string.lower(key) == "content-length" then
|
||||
length = tonumber(value)
|
||||
|
||||
if not length or math.floor(length) ~= length then
|
||||
return failcb(string.format("malformed server reply with header content-length (got %q, expected valid integer number)", value), true)
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if length then return cb(length) end
|
||||
|
||||
return pac.contentLengthFallback(url, cb, failcb)
|
||||
end, function(err, over400, code)
|
||||
if code == 405 then
|
||||
return pac.contentLengthFallback(url, cb, failcb)
|
||||
end
|
||||
|
||||
return failcb(err, over400)
|
||||
end )
|
||||
end
|
||||
|
||||
-- Performs a GET but requests 0 bytes
|
||||
-- We can then read the response headers to determine the content size.
|
||||
-- This allows Google Drive and other hosts to work with PAC even with content-length limits set
|
||||
-- (They typically block HEAD requests)
|
||||
function pac.contentLengthFallback(url, cb, failcb)
|
||||
local function fail()
|
||||
return failcb("unable to determine content length", true)
|
||||
end
|
||||
|
||||
return http("GET", url, {["Range"] = "bytes=0-0"}, function(data, data_length, headers)
|
||||
-- e.g. "bytes 0-0/11784402"
|
||||
local contentRange = headers["Content-Range"]
|
||||
if not contentRange then return fail() end
|
||||
|
||||
local spl = string.Split(contentRange, "/")
|
||||
local contentLength = spl[2]
|
||||
|
||||
if contentLength then return cb(tonumber(contentLength)) end
|
||||
|
||||
return fail()
|
||||
end )
|
||||
end
|
||||
|
||||
function pac.HTTPGet(url, cb, failcb)
|
||||
if not url or url:len() < 4 then
|
||||
failcb("url length is less than 4 (" .. tostring(url) .. ")", true)
|
||||
return
|
||||
end
|
||||
|
||||
url = pac.FixUrl(url)
|
||||
|
||||
local limit = SV_LIMIT:GetInt()
|
||||
|
||||
if CLIENT and (CL_LIMIT_OVERRIDE:GetBool() or limit == -1) then
|
||||
limit = CL_LIMIT:GetInt()
|
||||
end
|
||||
|
||||
if limit == -1 then
|
||||
return http("GET", url, nil, cb, failcb)
|
||||
end
|
||||
|
||||
return pac.getContentLength(url, function(length)
|
||||
if length then
|
||||
if length <= (limit * 1024) then
|
||||
http("GET", url, nil, cb, failcb)
|
||||
else
|
||||
failcb("download is too big (" .. string.NiceSize(length) .. ")", true)
|
||||
end
|
||||
else
|
||||
local allow_no_contentlength = SV_NO_CLENGTH:GetInt()
|
||||
|
||||
if CLIENT and (CL_LIMIT_OVERRIDE:GetBool() or allow_no_contentlength < 0) then
|
||||
allow_no_contentlength = CL_NO_CLENGTH:GetInt()
|
||||
end
|
||||
|
||||
if allow_no_contentlength > 0 then
|
||||
http("GET", url, nil, cb, failcb)
|
||||
else
|
||||
failcb("unknown file size when allow_no_contentlength is " .. allow_no_contentlength, true)
|
||||
end
|
||||
end
|
||||
end, failcb)
|
||||
end
|
||||
74
lua/pac3/core/shared/init.lua
Normal file
74
lua/pac3/core/shared/init.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
include("util.lua")
|
||||
|
||||
include("footsteps_fix.lua")
|
||||
include("http.lua")
|
||||
include("movement.lua")
|
||||
include("entity_mutator.lua")
|
||||
include("hash.lua")
|
||||
|
||||
pac.StringStream = include("pac3/libraries/string_stream.lua")
|
||||
|
||||
CreateConVar("pac_sv_draw_distance", 0, CLIENT and FCVAR_REPLICATED or bit.bor(FCVAR_REPLICATED, FCVAR_ARCHIVE))
|
||||
|
||||
do
|
||||
local tohash = {
|
||||
-- Crash
|
||||
'weapon_unusual_isotope.pcf',
|
||||
|
||||
-- Invalid
|
||||
'blood_fx.pcf',
|
||||
'boomer_fx.pcf',
|
||||
'charger_fx.pcf',
|
||||
'default.pcf',
|
||||
'electrical_fx.pcf',
|
||||
'environmental_fx.pcf',
|
||||
'fire_01l4d.pcf',
|
||||
'fire_fx.pcf',
|
||||
'fire_infected_fx.pcf',
|
||||
'firework_crate_fx.pcf',
|
||||
'fireworks_fx.pcf',
|
||||
'footstep_fx.pcf',
|
||||
'gen_dest_fx.pcf',
|
||||
'hunter_fx.pcf',
|
||||
'infected_fx.pcf',
|
||||
'insect_fx.pcf',
|
||||
'item_fx.pcf',
|
||||
'locator_fx.pcf',
|
||||
'military_artillery_impacts.pcf',
|
||||
'rain_fx.pcf',
|
||||
'rain_storm_fx.pcf',
|
||||
'rope_fx.pcf',
|
||||
'screen_fx.pcf',
|
||||
'smoker_fx.pcf',
|
||||
'speechbubbles.pcf',
|
||||
'spitter_fx.pcf',
|
||||
'steam_fx.pcf',
|
||||
'steamworks.pcf',
|
||||
'survivor_fx.pcf',
|
||||
'tank_fx.pcf',
|
||||
'tanker_explosion.pcf',
|
||||
'test_collision.pcf',
|
||||
'test_distancealpha.pcf',
|
||||
'ui_fx.pcf',
|
||||
'vehicle_fx.pcf',
|
||||
'water_fx.pcf',
|
||||
'weapon_fx.pcf',
|
||||
'witch_fx.pcf'
|
||||
}
|
||||
|
||||
pac.BlacklistedParticleSystems = {}
|
||||
|
||||
for i, val in ipairs(tohash) do
|
||||
pac.BlacklistedParticleSystems[val] = true
|
||||
end
|
||||
end
|
||||
324
lua/pac3/core/shared/movement.lua
Normal file
324
lua/pac3/core/shared/movement.lua
Normal file
@@ -0,0 +1,324 @@
|
||||
--[[
|
||||
| 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 movementConvar = CreateConVar("pac_free_movement", -1, CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "allow players to modify movement. -1 apply only allow when noclip is allowed, 1 allow for all gamemodes, 0 to disable")
|
||||
|
||||
local default = {
|
||||
JumpHeight = 200,
|
||||
StickToGround = true,
|
||||
GroundFriction = 0.12,
|
||||
AirFriction = 0.01,
|
||||
Gravity = Vector(0,0,-600),
|
||||
Noclip = false,
|
||||
MaxGroundSpeed = 750,
|
||||
MaxAirSpeed = 1,
|
||||
AllowZVelocity = false,
|
||||
ReversePitch = false,
|
||||
UnlockPitch = false,
|
||||
VelocityToViewAngles = 0,
|
||||
RollAmount = 0,
|
||||
|
||||
SprintSpeed = 750,
|
||||
RunSpeed = 300,
|
||||
WalkSpeed = 100,
|
||||
DuckSpeed = 25,
|
||||
|
||||
FinEfficiency = 0,
|
||||
FinLiftMode = "normal",
|
||||
FinCline = false
|
||||
}
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("pac_modify_movement")
|
||||
|
||||
net.Receive("pac_modify_movement", function(len, ply)
|
||||
local cvar = movementConvar:GetInt()
|
||||
if cvar == 0 or (cvar == -1 and hook.Run("PlayerNoClip", ply, true)==false) then return end
|
||||
|
||||
local str = net.ReadString()
|
||||
if str == "disable" then
|
||||
ply.pac_movement = nil
|
||||
else
|
||||
if default[str] ~= nil then
|
||||
local val = net.ReadType()
|
||||
if type(val) == type(default[str]) then
|
||||
ply.pac_movement = ply.pac_movement or table.Copy(default)
|
||||
ply.pac_movement[str] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local sensitivityConvar = GetConVar("sensitivity")
|
||||
pac.AddHook("InputMouseApply", "custom_movement", function(cmd, x,y, ang)
|
||||
local ply = pac.LocalPlayer
|
||||
local self = ply.pac_movement
|
||||
if not self then return end
|
||||
|
||||
if ply:GetMoveType() == MOVETYPE_NOCLIP then
|
||||
if ply.pac_movement_viewang then
|
||||
ang.r = 0
|
||||
cmd:SetViewAngles(ang)
|
||||
ply.pac_movement_viewang = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self.UnlockPitch then
|
||||
ply.pac_movement_viewang = ply.pac_movement_viewang or ang
|
||||
ang = ply.pac_movement_viewang
|
||||
|
||||
local sens = sensitivityConvar:GetFloat() * 20
|
||||
x = x / sens
|
||||
y = y / sens
|
||||
|
||||
if ang.p > 89 or ang.p < -89 then
|
||||
x = -x
|
||||
end
|
||||
|
||||
ang.p = math.NormalizeAngle(ang.p + y)
|
||||
ang.y = math.NormalizeAngle(ang.y + -x)
|
||||
end
|
||||
|
||||
if self.ReversePitch then
|
||||
ang.p = -ang.p
|
||||
end
|
||||
|
||||
local vel = ply:GetVelocity()
|
||||
|
||||
local roll = math.Clamp(vel:Dot(-ang:Right()) * self.RollAmount, -89, 89)
|
||||
if not vel:IsZero() then
|
||||
if vel:Dot(ang:Forward()) < 0 then
|
||||
vel = -vel
|
||||
end
|
||||
ang = LerpAngle(self.VelocityToViewAngles, ang, vel:Angle())
|
||||
end
|
||||
ang.r = roll
|
||||
|
||||
cmd:SetViewAngles(ang)
|
||||
|
||||
if self.UnlockPitch then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function badMovetype(ply)
|
||||
local mvtype = ply:GetMoveType()
|
||||
|
||||
return mvtype == MOVETYPE_OBSERVER
|
||||
or mvtype == MOVETYPE_NOCLIP
|
||||
or mvtype == MOVETYPE_LADDER
|
||||
or mvtype == MOVETYPE_CUSTOM
|
||||
or mvtype == MOVETYPE_ISOMETRIC
|
||||
end
|
||||
|
||||
local frictionConvar = GetConVar("sv_friction")
|
||||
pac.AddHook("Move", "custom_movement", function(ply, mv)
|
||||
local self = ply.pac_movement
|
||||
|
||||
if not self then
|
||||
if not ply.pac_custom_movement_reset then
|
||||
if not badMovetype(ply) then
|
||||
ply:SetGravity(1)
|
||||
ply:SetMoveType(MOVETYPE_WALK)
|
||||
|
||||
if ply.pac_custom_movement_jump_height then
|
||||
ply:SetJumpPower(ply.pac_custom_movement_jump_height)
|
||||
ply.pac_custom_movement_jump_height = nil
|
||||
end
|
||||
end
|
||||
|
||||
ply.pac_custom_movement_reset = true
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
ply.pac_custom_movement_reset = nil
|
||||
ply.pac_custom_movement_jump_height = ply.pac_custom_movement_jump_height or ply:GetJumpPower()
|
||||
|
||||
if badMovetype(ply) then return end
|
||||
|
||||
mv:SetForwardSpeed(0)
|
||||
mv:SetSideSpeed(0)
|
||||
mv:SetUpSpeed(0)
|
||||
|
||||
ply:SetJumpPower(self.JumpHeight)
|
||||
|
||||
if self.Noclip then
|
||||
ply:SetMoveType(MOVETYPE_NONE)
|
||||
else
|
||||
ply:SetMoveType(MOVETYPE_WALK)
|
||||
end
|
||||
|
||||
ply:SetGravity(0.00000000000000001)
|
||||
|
||||
local on_ground = ply:IsOnGround()
|
||||
|
||||
if not self.StickToGround then
|
||||
ply:SetGroundEntity(NULL)
|
||||
end
|
||||
|
||||
local speed = self.RunSpeed
|
||||
|
||||
if mv:KeyDown(IN_SPEED) then
|
||||
speed = self.SprintSpeed
|
||||
end
|
||||
|
||||
if mv:KeyDown(IN_WALK) then
|
||||
speed = self.WalkSpeed
|
||||
end
|
||||
|
||||
if mv:KeyDown(IN_DUCK) then
|
||||
speed = self.DuckSpeed
|
||||
end
|
||||
|
||||
-- speed = speed * FrameTime()
|
||||
|
||||
local ang = mv:GetAngles()
|
||||
local vel = Vector()
|
||||
|
||||
if on_ground and self.StickToGround then
|
||||
ang.p = 0
|
||||
end
|
||||
|
||||
if mv:KeyDown(IN_FORWARD) then
|
||||
vel = vel + ang:Forward()
|
||||
elseif mv:KeyDown(IN_BACK) then
|
||||
vel = vel - ang:Forward()
|
||||
end
|
||||
|
||||
if mv:KeyDown(IN_MOVERIGHT) then
|
||||
vel = vel + ang:Right()
|
||||
elseif mv:KeyDown(IN_MOVELEFT) then
|
||||
vel = vel - ang:Right()
|
||||
end
|
||||
|
||||
vel = vel:GetNormalized() * speed
|
||||
|
||||
if self.AllowZVelocity then
|
||||
if mv:KeyDown(IN_JUMP) then
|
||||
vel = vel + ang:Up() * speed
|
||||
elseif mv:KeyDown(IN_DUCK) then
|
||||
vel = vel - ang:Up() * speed
|
||||
end
|
||||
end
|
||||
|
||||
if not self.AllowZVelocity then
|
||||
vel.z = 0
|
||||
end
|
||||
|
||||
local speed = vel
|
||||
|
||||
local vel = mv:GetVelocity()
|
||||
|
||||
if on_ground and not self.Noclip and self.StickToGround then -- work against ground friction
|
||||
local sv_friction = frictionConvar:GetInt()
|
||||
|
||||
if sv_friction > 0 then
|
||||
sv_friction = 1 - (sv_friction * 15) / 1000
|
||||
vel = vel / sv_friction
|
||||
end
|
||||
end
|
||||
|
||||
vel = vel + self.Gravity * 0
|
||||
|
||||
-- todo: don't allow adding more velocity to existing velocity if it exceeds
|
||||
-- but allow decreasing
|
||||
if not on_ground then
|
||||
local friction = self.AirFriction
|
||||
friction = -(friction) + 1
|
||||
|
||||
vel = vel * friction
|
||||
|
||||
vel = vel + self.Gravity * 0.015
|
||||
speed = speed:GetNormalized() * math.Clamp(speed:Length(), 0, self.MaxAirSpeed)
|
||||
vel = vel + (speed * FrameTime()*(66.666*(-friction+1)))
|
||||
else
|
||||
local friction = self.GroundFriction
|
||||
friction = -(friction) + 1
|
||||
|
||||
vel = vel * friction
|
||||
|
||||
speed = speed:GetNormalized() * math.min(speed:Length(), self.MaxGroundSpeed)
|
||||
vel = vel + (speed * FrameTime()*(75.77*(-friction+1)))
|
||||
vel = vel + self.Gravity * 0.015
|
||||
end
|
||||
|
||||
if self.FinEfficiency > 0 then -- fin
|
||||
local curvel = vel
|
||||
local curup = ang:Forward()
|
||||
|
||||
local vec1 = curvel
|
||||
local vec2 = curup
|
||||
vec1 = vec1 - 2*(vec1:Dot(vec2))*vec2
|
||||
local sped = vec1:Length()
|
||||
|
||||
local finalvec = curvel
|
||||
local modf = math.abs(curup:Dot(curvel:GetNormalized()))
|
||||
local nvec = (curup:Dot(curvel:GetNormalized()))
|
||||
|
||||
if (self.pln == 1) then
|
||||
|
||||
if nvec > 0 then
|
||||
vec1 = vec1 + (curup * 10)
|
||||
else
|
||||
vec1 = vec1 + (curup * -10)
|
||||
end
|
||||
|
||||
finalvec = vec1:GetNormalized() * (math.pow(sped, modf) - 1)
|
||||
finalvec = finalvec:GetNormalized()
|
||||
finalvec = (finalvec * self.FinEfficiency) + curvel
|
||||
end
|
||||
|
||||
if (self.FinLiftMode ~= "none") then
|
||||
if (self.FinLiftMode == "normal") then
|
||||
local liftmul = 1 - math.abs(nvec)
|
||||
finalvec = finalvec + (curup * liftmul * curvel:Length() * self.FinEfficiency) / 700
|
||||
else
|
||||
local liftmul = (nvec / math.abs(nvec)) - nvec
|
||||
finalvec = finalvec + (curup * curvel:Length() * self.FinEfficiency * liftmul) / 700
|
||||
end
|
||||
end
|
||||
|
||||
finalvec = finalvec:GetNormalized()
|
||||
finalvec = finalvec * curvel:Length()
|
||||
|
||||
if self.FinCline then
|
||||
local trace = {
|
||||
start = mv:GetOrigin(),
|
||||
endpos = mv:GetOrigin() + Vector(0, 0, -1000000),
|
||||
mask = 131083
|
||||
}
|
||||
local trc = util.TraceLine(trace)
|
||||
|
||||
local MatType = trc.MatType
|
||||
|
||||
if (MatType == 67 or MatType == 77) then
|
||||
local heatvec = Vector(0, 0, 100)
|
||||
local cline = ((2 * (heatvec:Dot(curup)) * curup - heatvec)) * (math.abs(heatvec:Dot(curup)) / 1000)
|
||||
finalvec = finalvec + (cline * (self.FinEfficiency / 50))
|
||||
end
|
||||
end
|
||||
|
||||
vel = finalvec
|
||||
end
|
||||
|
||||
mv:SetVelocity(vel)
|
||||
|
||||
if self.Noclip then
|
||||
mv:SetOrigin(mv:GetOrigin() + vel * 0.01)
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
||||
784
lua/pac3/core/shared/util.lua
Normal file
784
lua/pac3/core/shared/util.lua
Normal file
@@ -0,0 +1,784 @@
|
||||
--[[
|
||||
| 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 PREFIX = '[PAC3] '
|
||||
local PREFIX_COLOR = Color(255, 255, 0)
|
||||
|
||||
local DEFAULT_TEXT_COLOR = Color(200, 200, 200)
|
||||
local BOOLEAN_COLOR = Color(33, 83, 226)
|
||||
local NUMBER_COLOR = Color(245, 199, 64)
|
||||
local STEAMID_COLOR = Color(255, 255, 255)
|
||||
local ENTITY_COLOR = Color(180, 232, 180)
|
||||
local FUNCTION_COLOR = Color(62, 106, 255)
|
||||
local TABLE_COLOR = Color(107, 200, 224)
|
||||
local URL_COLOR = Color(174, 124, 192)
|
||||
|
||||
function pac.RepackMessage(strIn)
|
||||
local output = {}
|
||||
|
||||
for line in string.gmatch(strIn, '([^ ]+)') do
|
||||
if #output ~= 0 then
|
||||
table.insert(output, ' ')
|
||||
end
|
||||
|
||||
table.insert(output, line)
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
local function FormatMessage(tabIn)
|
||||
local prevColor = DEFAULT_TEXT_COLOR
|
||||
local output = {prevColor}
|
||||
|
||||
for i, val in ipairs(tabIn) do
|
||||
local valType = type(val)
|
||||
|
||||
if valType == 'number' then
|
||||
table.insert(output, NUMBER_COLOR)
|
||||
table.insert(output, tostring(val))
|
||||
table.insert(output, prevColor)
|
||||
elseif valType == 'string' then
|
||||
if val:find('^https?://') then
|
||||
table.insert(output, URL_COLOR)
|
||||
table.insert(output, val)
|
||||
table.insert(output, prevColor)
|
||||
else
|
||||
table.insert(output, val)
|
||||
end
|
||||
elseif valType == 'Player' then
|
||||
if team then
|
||||
table.insert(output, team.GetColor(val:Team()) or ENTITY_COLOR)
|
||||
else
|
||||
table.insert(output, ENTITY_COLOR)
|
||||
end
|
||||
|
||||
table.insert(output, val:Nick())
|
||||
|
||||
if val.SteamName and val:SteamName() ~= val:Nick() then
|
||||
table.insert(output, ' (' .. val:SteamName() .. ')')
|
||||
end
|
||||
|
||||
table.insert(output, '<')
|
||||
table.insert(output, val:SteamID())
|
||||
table.insert(output, '>')
|
||||
table.insert(output, prevColor)
|
||||
elseif valType == 'Entity' or valType == 'NPC' or valType == 'Vehicle' then
|
||||
table.insert(output, ENTITY_COLOR)
|
||||
table.insert(output, tostring(val))
|
||||
table.insert(output, prevColor)
|
||||
elseif IsColor(val) then
|
||||
table.insert(output, val)
|
||||
prevColor = val
|
||||
elseif valType == 'table' then
|
||||
table.insert(output, TABLE_COLOR)
|
||||
table.insert(output, tostring(val))
|
||||
table.insert(output, prevColor)
|
||||
elseif valType == 'function' then
|
||||
table.insert(output, FUNCTION_COLOR)
|
||||
table.insert(output, string.format('function - %p', val))
|
||||
table.insert(output, prevColor)
|
||||
elseif valType == 'boolean' then
|
||||
table.insert(output, BOOLEAN_COLOR)
|
||||
table.insert(output, tostring(val))
|
||||
table.insert(output, prevColor)
|
||||
else
|
||||
table.insert(output, tostring(val))
|
||||
end
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
pac.FormatMessage = FormatMessage
|
||||
|
||||
function pac.Message(...)
|
||||
local formatted = FormatMessage({...})
|
||||
MsgC(PREFIX_COLOR, PREFIX, unpack(formatted))
|
||||
MsgC('\n')
|
||||
return formatted
|
||||
end
|
||||
|
||||
function pac.dprint(fmt, ...)
|
||||
if not pac.debug then return end
|
||||
MsgN("\n")
|
||||
MsgN(">>>PAC3>>>")
|
||||
MsgN(fmt:format(...))
|
||||
if pac.debug_trace then
|
||||
MsgN("==TRACE==")
|
||||
debug.Trace()
|
||||
MsgN("==TRACE==")
|
||||
end
|
||||
MsgN("<<<PAC3<<<")
|
||||
MsgN("\n")
|
||||
end
|
||||
|
||||
local DEBUG_MDL = false
|
||||
local VERBOSE = false
|
||||
|
||||
local shader_params = include("pac3/libraries/shader_params.lua")
|
||||
local texture_keys = {}
|
||||
|
||||
for _, shader in pairs(shader_params.shaders) do
|
||||
for _, params in pairs(shader) do
|
||||
for key, info in pairs(params) do
|
||||
if info.type == "texture" then
|
||||
texture_keys[key] = key
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, params in pairs(shader_params.base) do
|
||||
for key, info in pairs(params) do
|
||||
if info.type == "texture" then
|
||||
texture_keys[key] = key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
texture_keys["include"] = "include"
|
||||
|
||||
-- for pac_restart
|
||||
PAC_MDL_SALT = PAC_MDL_SALT or 0
|
||||
|
||||
local cached_paths = {}
|
||||
|
||||
function pac.DownloadMDL(url, callback, onfail, ply)
|
||||
local skip_cache = false
|
||||
|
||||
if url:StartWith("_") then
|
||||
skip_cache = true
|
||||
url = url:sub(2)
|
||||
end
|
||||
|
||||
if not skip_cache and cached_paths[url] then
|
||||
callback(cached_paths[url])
|
||||
return
|
||||
end
|
||||
|
||||
return pac.resource.Download(url, function(path)
|
||||
if ply:IsPlayer() and not ply:IsValid() then
|
||||
pac.Message(Color(255, 50, 50), "player is no longer valid")
|
||||
file.Delete(path)
|
||||
return
|
||||
end
|
||||
|
||||
local file_content = file.Read(path)
|
||||
|
||||
if not file_content then
|
||||
pac.Message(Color(255, 50, 50), "content is empty")
|
||||
file.Delete(path)
|
||||
return
|
||||
end
|
||||
|
||||
local id = util.CRC(url .. file_content)
|
||||
|
||||
if skip_cache then
|
||||
id = util.CRC(id .. os.clock())
|
||||
end
|
||||
|
||||
if skip_cache or not file.Exists("pac3_cache/downloads/"..id..".dat", "DATA") then
|
||||
|
||||
local dir = "pac3_cache/" .. id .. "/"
|
||||
|
||||
local f = file.Open(path, "rb", "DATA")
|
||||
|
||||
local files = {}
|
||||
|
||||
local ok, err = pcall(function()
|
||||
for i = 1, 128 do
|
||||
local pos = f:Tell()
|
||||
|
||||
local sig = f:ReadLong()
|
||||
|
||||
if sig == 0x02014b50 then break end
|
||||
|
||||
assert(sig == 0x04034b50, "bad zip signature (file is not a zip?)")
|
||||
|
||||
f:Seek(pos+6) local bitflag = f:ReadShort()
|
||||
f:Seek(pos+8) local compression_method = f:ReadShort()
|
||||
f:Seek(pos+14) local crc = f:ReadShort()
|
||||
f:Seek(pos+18) local size2 = f:ReadLong()
|
||||
f:Seek(pos+22) local size = f:ReadLong()
|
||||
f:Seek(pos+26) local file_name_length = f:ReadShort()
|
||||
local extra_field_length = f:ReadShort()
|
||||
|
||||
local name = f:Read(file_name_length):lower()
|
||||
local file_path = name
|
||||
|
||||
if compression_method ~= 0 then
|
||||
error("the file " .. name .. " is compressed! (use compression method 0 / store, or maybe you drag dropped files into the archive)")
|
||||
end
|
||||
|
||||
f:Skip(extra_field_length)
|
||||
|
||||
local buffer = f:Read(size)
|
||||
|
||||
name = name:match(".+/(.+)") or name
|
||||
|
||||
if not buffer then
|
||||
if not file_path:EndsWith("/") then
|
||||
pac.Message(Color(255, 50,50), file_path .. " is empty")
|
||||
end
|
||||
else
|
||||
local ok = true
|
||||
for i,v in ipairs(files) do
|
||||
if v.file_name == name then
|
||||
if ply == pac.LocalPlayer then
|
||||
pac.Message(Color(255, 50,50), file_path .. " is already a file at " .. v.file_path)
|
||||
end
|
||||
ok = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if ok then
|
||||
table.insert(files, {file_name = name, buffer = buffer, crc = crc, file_path = file_path})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
f:Close()
|
||||
|
||||
local count = 0
|
||||
local model_found = false
|
||||
local other_models = {}
|
||||
|
||||
table.sort(files, function(a, b) return #a.buffer > #b.buffer end)
|
||||
|
||||
for i, v in ipairs(files) do
|
||||
if v.file_name:EndsWith(".mdl") then
|
||||
local name = v.file_name:match("(.+)%.mdl")
|
||||
for _, v2 in ipairs(files) do
|
||||
if v2.file_name:EndsWith(name .. ".ani") then
|
||||
v.ani = v2
|
||||
break
|
||||
end
|
||||
end
|
||||
if v.ani then
|
||||
v.file_name = v.file_name:gsub(".-(%..+)", "i"..count.."%1"):lower()
|
||||
v.ani.file_name = v.ani.file_name:gsub(".-(%..+)", "i"..count.."%1"):lower()
|
||||
count = count + 1
|
||||
else
|
||||
if not model_found or v.file_name:StartWith(model_found) then
|
||||
model_found = v.file_name:match("(.-)%.")
|
||||
v.file_name = v.file_name:gsub(".-(%..+)", "model%1"):lower()
|
||||
else
|
||||
table.insert(other_models, v.file_name)
|
||||
end
|
||||
end
|
||||
elseif v.file_name:EndsWith(".vtx") or v.file_name:EndsWith(".vvd") or v.file_name:EndsWith(".phy") then
|
||||
if not model_found or v.file_name:StartWith(model_found) then
|
||||
model_found = v.file_name:match("(.-)%.")
|
||||
v.file_name = v.file_name:gsub(".-(%..+)", "model%1"):lower()
|
||||
else
|
||||
table.insert(other_models, v.file_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if other_models[1] and ply == pac.LocalPlayer then
|
||||
pac.Message(Color(255, 200, 50), url, ": the archive contains more than one model.")
|
||||
pac.Message(Color(255, 200, 50), url, ": " .. model_found .. " was selected.")
|
||||
pac.Message(Color(255, 200, 50), url, ": these are ignored:")
|
||||
PrintTable(other_models)
|
||||
end
|
||||
|
||||
if VERBOSE then
|
||||
print("FILES:")
|
||||
for i, v in ipairs(files) do
|
||||
print(v.file_name)
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then
|
||||
onfail(err)
|
||||
local str = file.Read(path)
|
||||
file.Delete(path)
|
||||
|
||||
pac.Message(Color(255, 50,50), err)
|
||||
pac.Message(Color(255, 50,50), "the zip archive downloaded (", string.NiceSize(#str) ,") could not be parsed")
|
||||
|
||||
local is_binary = false
|
||||
for i = 1, #str do
|
||||
local b = str:byte(i)
|
||||
if b == 0 then
|
||||
is_binary = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not is_binary then
|
||||
pac.Message(Color(255, 50,50), "the url isn't a binary zip archive. Is it a html website? here's the content:")
|
||||
print(str)
|
||||
elseif ply == pac.LocalPlayer then
|
||||
file.Write("pac3_cache/failed_zip_download.dat", str)
|
||||
pac.Message("the zip archive was stored to garrysmod/data/pac3_cache/failed_zip_download.dat (rename extension to .zip) if you want to inspect it")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local required = {
|
||||
".mdl",
|
||||
".vvd",
|
||||
".dx90.vtx",
|
||||
}
|
||||
local found = {}
|
||||
for k,v in pairs(files) do
|
||||
for _, ext in ipairs(required) do
|
||||
if v.file_name:EndsWith(ext) then
|
||||
table.insert(found, ext)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #found < #required then
|
||||
local str = {}
|
||||
|
||||
for _, ext in ipairs(required) do
|
||||
if not table.HasValue(found, ext) then
|
||||
table.insert(str, ext)
|
||||
end
|
||||
end
|
||||
|
||||
onfail("could not find " .. table.concat(str, " or ") .. " in zip archive")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
do -- hex models
|
||||
local found_vmt_directories = {}
|
||||
|
||||
for i, data in ipairs(files) do
|
||||
if data.file_name:EndsWith(".mdl") then
|
||||
local found_materials = {}
|
||||
local found_materialdirs = {}
|
||||
local found_mdl_includes = {}
|
||||
|
||||
local vtf_dir_offset
|
||||
local vtf_dir_count
|
||||
|
||||
local material_offset
|
||||
local material_count
|
||||
|
||||
local include_mdl_dir_offset
|
||||
local include_mdl_dir_count
|
||||
|
||||
if DEBUG_MDL then
|
||||
file.Write(data.file_name..".debug.old.dat", data.buffer)
|
||||
end
|
||||
|
||||
local f = pac.StringStream(data.buffer)
|
||||
local id = f:read(4)
|
||||
local version = f:readUInt32()
|
||||
local checksum = f:readUInt32()
|
||||
|
||||
local name_offset = f:tell()
|
||||
local name = f:read(64)
|
||||
local size_offset = f:tell()
|
||||
local size = f:readUInt32()
|
||||
|
||||
f:skip(12 * 6) -- skips over all the vec3 stuff
|
||||
|
||||
f:skip(4) -- flags
|
||||
f:skip(8) -- bone
|
||||
f:skip(8) -- bone controller
|
||||
f:skip(8) -- hitbox
|
||||
f:skip(8) -- local anim
|
||||
f:skip(8) -- sequences
|
||||
f:skip(8) -- activitylistversion + eventsindexed
|
||||
|
||||
do
|
||||
material_count = f:readUInt32()
|
||||
material_offset = f:readUInt32() + 1 -- +1 to convert 0 indexed to 1 indexed
|
||||
|
||||
local old_pos = f:tell()
|
||||
f:seek(material_offset)
|
||||
|
||||
for i = 1, material_count do
|
||||
local material_start = f:tell()
|
||||
local material_name_offset = f:readInt32()
|
||||
f:skip(60)
|
||||
local material_end = f:tell()
|
||||
|
||||
local material_name_pos = material_start + material_name_offset
|
||||
f:seek(material_name_pos)
|
||||
|
||||
local material_name = (f:readString() .. ".vmt"):lower()
|
||||
local found = false
|
||||
|
||||
for i, v in pairs(files) do
|
||||
if v.file_name == material_name then
|
||||
found = v.file_path
|
||||
break
|
||||
elseif v.file_path == ("materials/" .. material_name) then
|
||||
v.file_name = material_name
|
||||
found = v.file_path
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
for i, v in pairs(files) do
|
||||
if string.find(v.file_path, material_name, 1, true) or string.find(material_name, v.file_name, 1, true) then
|
||||
table.insert(files, {file_name = material_name, buffer = v.buffer, crc = v.crc, file_path = v.file_path})
|
||||
found = v.file_path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
if ply == pac.LocalPlayer then
|
||||
pac.Message(Color(255, 50,50), url, " the model wants to find ", material_name , " but it was not found in the zip archive")
|
||||
end
|
||||
local dummy = "VertexLitGeneric\n{\n\t$basetexture \"error\"\n}"
|
||||
table.insert(files, {file_name = material_name, buffer = dummy, crc = util.CRC(dummy), file_path = material_name})
|
||||
end
|
||||
|
||||
table.insert(found_materials, {name = material_name, offset = material_name_pos})
|
||||
f:seek(material_end)
|
||||
end
|
||||
|
||||
if ply == pac.LocalPlayer and #found_materials == 0 then
|
||||
pac.Message(Color(255, 200, 50), url, ": could not find any materials in this model")
|
||||
end
|
||||
|
||||
f:seek(old_pos)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
vtf_dir_count = f:readUInt32()
|
||||
vtf_dir_offset = f:readUInt32() + 1 -- +1 to convert 0 indexed to 1 indexed
|
||||
|
||||
local old_pos = f:tell()
|
||||
f:seek(vtf_dir_offset)
|
||||
for i = 1, vtf_dir_count do
|
||||
local offset_pos = f:tell()
|
||||
local offset = f:readUInt32() + 1 -- +1 to convert 0 indexed to 1 indexed
|
||||
|
||||
local old_pos = f:tell()
|
||||
f:seek(offset)
|
||||
local dir = f:readString()
|
||||
table.insert(found_materialdirs, {offset_pos = offset_pos, offset = offset, dir = dir})
|
||||
table.insert(found_vmt_directories, {dir = dir})
|
||||
f:seek(old_pos)
|
||||
end
|
||||
table.sort(found_vmt_directories, function(a,b) return #a.dir>#b.dir end)
|
||||
f:seek(old_pos)
|
||||
end
|
||||
|
||||
f:skip(4 + 8) -- skin
|
||||
f:skip(8) -- bodypart
|
||||
f:skip(8) -- attachment
|
||||
f:skip(4 + 8) -- localnode
|
||||
f:skip(8) -- flex
|
||||
f:skip(8) -- flex rules
|
||||
f:skip(8) -- ik
|
||||
f:skip(8) -- mouth
|
||||
f:skip(8) -- localpose
|
||||
f:skip(4) -- render2dprop
|
||||
f:skip(8) -- keyvalues
|
||||
f:skip(8) -- iklock
|
||||
f:skip(12) -- mass
|
||||
f:skip(4) -- contents
|
||||
|
||||
do
|
||||
include_mdl_dir_count = f:readUInt32()
|
||||
include_mdl_dir_offset = f:readUInt32() + 1 -- +1 to convert 0 indexed to 1 indexed
|
||||
|
||||
local old_pos = f:tell()
|
||||
|
||||
f:seek(include_mdl_dir_offset)
|
||||
for i = 1, include_mdl_dir_count do
|
||||
local base_pos = f:tell()
|
||||
|
||||
f:skip(4)
|
||||
|
||||
local file_name_offset = f:readUInt32()
|
||||
local old_pos = f:tell()
|
||||
f:seek(base_pos + file_name_offset)
|
||||
table.insert(found_mdl_includes, {base_pos = base_pos, path = f:readString()})
|
||||
f:seek(old_pos)
|
||||
end
|
||||
|
||||
f:seek(old_pos)
|
||||
end
|
||||
|
||||
f:skip(4) -- virtual pointer
|
||||
|
||||
local anim_name_offset_pos = f:tell()
|
||||
|
||||
if VERBOSE or DEBUG_MDL then
|
||||
print(data.file_name, "MATERIAL DIRECTORIES:")
|
||||
PrintTable(found_materialdirs)
|
||||
print("============")
|
||||
print(data.file_name, "MATERIALS:")
|
||||
PrintTable(found_materials)
|
||||
print("============")
|
||||
print(data.file_name, "MDL_INCLUDES:")
|
||||
PrintTable(found_mdl_includes)
|
||||
print("============")
|
||||
end
|
||||
|
||||
do -- replace the mdl name (max size is 64 bytes)
|
||||
local newname = string.sub(dir .. data.file_name:lower(), 1, 63)
|
||||
f:seek(name_offset)
|
||||
f:write(newname .. string.rep("\0", 64-#newname))
|
||||
end
|
||||
|
||||
for i,v in ipairs(found_mdl_includes) do
|
||||
local file_name = (v.path:match(".+/(.+)") or v.path)
|
||||
local found = false
|
||||
|
||||
for _, info in ipairs(files) do
|
||||
if info.file_path == file_name then
|
||||
file_name = info.file_name
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if found then
|
||||
local path = "models/" .. dir .. file_name
|
||||
local newoffset = f:size() + 1
|
||||
f:seek(newoffset)
|
||||
f:writeString(path)
|
||||
f:seek(v.base_pos + 4)
|
||||
f:writeInt32(newoffset - v.base_pos)
|
||||
elseif ply == pac.LocalPlayer and not file.Exists(v.path, "GAME") then
|
||||
pac.Message(Color(255, 50, 50), "the model want to include ", v.path, " but it doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
-- if we extend the mdl file with vmt directories we don't have to change any offsets cause nothing else comes after it
|
||||
if data.file_name == "model.mdl" then
|
||||
for i,v in ipairs(found_materialdirs) do
|
||||
local newoffset = f:size() + 1
|
||||
f:seek(newoffset)
|
||||
f:writeString(dir)
|
||||
f:seek(v.offset_pos)
|
||||
f:writeInt32(newoffset - 1) -- -1 to convert 1 indexed to 0 indexed
|
||||
end
|
||||
else
|
||||
local new_name = "models/" .. dir .. data.file_name:gsub("mdl$", "ani")
|
||||
local newoffset = f:size() + 1
|
||||
f:seek(newoffset)
|
||||
f:writeString(new_name)
|
||||
f:seek(anim_name_offset_pos)
|
||||
f:writeInt32(newoffset - 1) -- -1 to convert 1 indexed to 0 indexed
|
||||
end
|
||||
|
||||
local cursize = f:size()
|
||||
|
||||
-- Add nulls to align to 4 bytes
|
||||
local padding = 4-cursize%4
|
||||
if padding<4 then
|
||||
f:seek(cursize+1)
|
||||
f:write(string.rep("\0",padding))
|
||||
cursize = cursize + padding
|
||||
end
|
||||
|
||||
f:seek(size_offset)
|
||||
f:writeInt32(cursize)
|
||||
|
||||
data.buffer = f:getString()
|
||||
|
||||
if DEBUG_MDL then
|
||||
file.Write(data.file_name..".debug.new.dat", data.buffer)
|
||||
end
|
||||
|
||||
local crc = pac.StringStream()
|
||||
crc:writeInt32(tonumber(util.CRC(data.buffer)))
|
||||
data.crc = crc:getString()
|
||||
end
|
||||
end
|
||||
|
||||
for i, data in ipairs(files) do
|
||||
if data.file_name:EndsWith(".vmt") then
|
||||
local proxies = data.buffer:match('("?%f[%w_]P?p?roxies%f[^%w_]"?%s*%b{})')
|
||||
data.buffer = data.buffer:lower():gsub("\\", "/")
|
||||
|
||||
if proxies then
|
||||
data.buffer = data.buffer:gsub('("?%f[%w_]proxies%f[^%w_]"?%s*%b{})', proxies)
|
||||
end
|
||||
|
||||
if DEBUG_MDL or VERBOSE then
|
||||
print(data.file_name .. ":")
|
||||
end
|
||||
|
||||
for shader_param in pairs(texture_keys) do
|
||||
data.buffer = data.buffer:gsub('("?%$?%f[%w_]' .. shader_param .. '%f[^%w_]"?%s+"?)([^"%c]+)("?%s?)', function(l, vtf_path, r)
|
||||
if vtf_path == "env_cubemap" then
|
||||
return
|
||||
end
|
||||
|
||||
local new_path
|
||||
for _, info in ipairs(found_vmt_directories) do
|
||||
if info.dir == "" then continue end
|
||||
|
||||
new_path, count = vtf_path:gsub("^" .. info.dir:gsub("\\", "/"):lower(), dir)
|
||||
if count == 0 then
|
||||
new_path = nil
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not new_path then
|
||||
for _, info in ipairs(files) do
|
||||
local vtf_name = (vtf_path:match(".+/(.+)") or vtf_path)
|
||||
if info.file_name:EndsWith(".vtf") then
|
||||
if info.file_name == vtf_name .. ".vtf" or info.file_name == vtf_name then
|
||||
new_path = dir .. vtf_name
|
||||
break
|
||||
end
|
||||
elseif (info.file_name:EndsWith(".vmt") and l:StartWith("include")) then
|
||||
if info.file_name == vtf_name then
|
||||
new_path = "materials/" .. dir .. vtf_name
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not new_path then
|
||||
if not file.Exists("materials/" .. vtf_path .. ".vtf", "GAME") then
|
||||
if ply == pac.LocalPlayer then
|
||||
pac.Message(Color(255, 50, 50), "vmt ", data.file_name, " wants to find texture materials/", vtf_path, ".vtf for $", shader_param ," but it doesn't exist")
|
||||
print(data.buffer)
|
||||
end
|
||||
end
|
||||
new_path = vtf_path -- maybe it's a special texture? in that case i need to it
|
||||
end
|
||||
|
||||
if DEBUG_MDL or VERBOSE then
|
||||
print("\t" .. vtf_path .. " >> " .. new_path)
|
||||
end
|
||||
|
||||
return l .. new_path .. r
|
||||
end)
|
||||
end
|
||||
|
||||
local crc = pac.StringStream()
|
||||
crc:writeInt32(tonumber(util.CRC(data.buffer)))
|
||||
data.crc = crc:getString()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if skip_cache then
|
||||
id = id .. "_temp"
|
||||
end
|
||||
|
||||
local path = "pac3_cache/downloads/" .. id .. ".dat"
|
||||
local f = file.Open(path, "wb", "DATA")
|
||||
|
||||
if not f then
|
||||
onfail("unable to open file " .. path .. " for writing")
|
||||
|
||||
pac.Message(Color(255, 50, 50), "unable to write to ", path, " for some reason")
|
||||
if file.Exists(path, "DATA") then
|
||||
pac.Message(Color(255, 50, 50), "the file exists and its size is ", string.NiceSize(file.Size(path, "DATA")))
|
||||
pac.Message(Color(255, 50, 50), "is it locked or in use by something else?")
|
||||
else
|
||||
pac.Message(Color(255, 50, 50), "the file does not exist")
|
||||
pac.Message(Color(255, 50, 50), "are you out of disk space?")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
f:Write("GMAD")
|
||||
f:WriteByte(3)
|
||||
f:WriteLong(0)f:WriteLong(0)
|
||||
f:WriteLong(0)f:WriteLong(0)
|
||||
f:WriteByte(0)
|
||||
f:Write("name here")f:WriteByte(0)
|
||||
f:Write("description here")f:WriteByte(0)
|
||||
f:Write("author here")f:WriteByte(0)
|
||||
f:WriteLong(1)
|
||||
|
||||
for i, data in ipairs(files) do
|
||||
f:WriteLong(i)
|
||||
if data.file_name:EndsWith(".vtf") or data.file_name:EndsWith(".vmt") then
|
||||
f:Write("materials/" .. dir .. data.file_name:lower())f:WriteByte(0)
|
||||
else
|
||||
f:Write("models/" .. dir .. data.file_name:lower())f:WriteByte(0)
|
||||
end
|
||||
f:WriteLong(#data.buffer)f:WriteLong(0)
|
||||
f:WriteLong(data.crc)
|
||||
end
|
||||
|
||||
f:WriteLong(0)
|
||||
|
||||
for i, data in ipairs(files) do
|
||||
f:Write(data.buffer)
|
||||
end
|
||||
|
||||
f:Flush()
|
||||
|
||||
local content = file.Read("pac3_cache/downloads/" .. id .. ".dat", "DATA")
|
||||
f:Write(util.CRC(content))
|
||||
f:Close()
|
||||
end
|
||||
|
||||
local ok, tbl = game.MountGMA("data/pac3_cache/downloads/" .. id .. ".dat")
|
||||
|
||||
if not ok then
|
||||
onfail("failed to mount gma mdl")
|
||||
return
|
||||
end
|
||||
|
||||
for k,v in pairs(tbl) do
|
||||
if v:EndsWith("model.mdl") then
|
||||
if VERBOSE and not DEBUG_MDL then
|
||||
print("util.IsValidModel: ", tostring(util.IsValidModel(v)))
|
||||
|
||||
local dev = GetConVar("developer"):GetFloat()
|
||||
if dev == 0 then
|
||||
RunConsoleCommand("developer", "3")
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if CLIENT then
|
||||
ClientsideModel(v):Remove()
|
||||
else
|
||||
local ent = ents.Create("prop_dynamic")
|
||||
ent:SetModel(v)
|
||||
ent:Spawn()
|
||||
ent:Remove()
|
||||
end
|
||||
print("created and removed model")
|
||||
RunConsoleCommand("developer", "0")
|
||||
end)
|
||||
else
|
||||
if CLIENT then
|
||||
ClientsideModel(v):Remove()
|
||||
else
|
||||
local ent = ents.Create("prop_dynamic")
|
||||
ent:SetModel(v)
|
||||
ent:Spawn()
|
||||
ent:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cached_paths[url] = v
|
||||
callback(v)
|
||||
|
||||
file.Delete("pac3_cache/downloads/" .. id .. ".dat")
|
||||
break
|
||||
end
|
||||
end
|
||||
end, onfail)
|
||||
end
|
||||
578
lua/pac3/editor/client/about.lua
Normal file
578
lua/pac3/editor/client/about.lua
Normal file
@@ -0,0 +1,578 @@
|
||||
--[[
|
||||
| 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 DEMO = {}
|
||||
|
||||
DEMO.Title = "Sand"
|
||||
DEMO.Author = "Capsadmin"
|
||||
|
||||
local lines =
|
||||
{
|
||||
--surface.GetTextureID("sprites/laser"),
|
||||
--surface.GetTextureID("sprites/bluelaser"),
|
||||
surface.GetTextureID("effects/laser1"),
|
||||
surface.GetTextureID("trails/laser"),
|
||||
}
|
||||
|
||||
local sprites =
|
||||
{
|
||||
surface.GetTextureID("particle/fire"),
|
||||
}
|
||||
|
||||
local white = surface.GetTextureID("vgui/white")
|
||||
|
||||
function DEMO:DrawLineEx(x1,y1, x2,y2, w, skip_tex)
|
||||
w = w or 1
|
||||
if not skip_tex then surface.SetTexture(white) end
|
||||
|
||||
local dx,dy = x1-x2, y1-y2
|
||||
local ang = math.atan2(dx, dy)
|
||||
local dst = math.sqrt((dx * dx) + (dy * dy))
|
||||
|
||||
x1 = x1 - dx * 0.5
|
||||
y1 = y1 - dy * 0.5
|
||||
|
||||
surface.DrawTexturedRectRotated(x1, y1, w, dst, math.deg(ang))
|
||||
end
|
||||
|
||||
do
|
||||
local fonts = {}
|
||||
|
||||
local function create_fonts(font, size, weight, blursize)
|
||||
local main = "pretty_text_" .. size .. weight
|
||||
local blur = "pretty_text_blur_" .. size .. weight
|
||||
|
||||
surface.CreateFont(
|
||||
main,
|
||||
{
|
||||
font = font,
|
||||
size = size,
|
||||
weight = weight,
|
||||
antialias = true,
|
||||
additive = true,
|
||||
}
|
||||
)
|
||||
|
||||
surface.CreateFont(
|
||||
blur,
|
||||
{
|
||||
font = font,
|
||||
size = size,
|
||||
weight = weight,
|
||||
antialias = true,
|
||||
blursize = blursize,
|
||||
}
|
||||
)
|
||||
|
||||
return
|
||||
{
|
||||
main = main,
|
||||
blur = blur,
|
||||
}
|
||||
end
|
||||
|
||||
local def_color1 = Color(255, 255, 255, 255)
|
||||
local def_color2 = Color(0, 0, 0, 255)
|
||||
|
||||
local surface_SetFont = surface.SetFont
|
||||
local surface_SetTextColor = surface.SetTextColor
|
||||
local surface_SetTextPos = surface.SetTextPos
|
||||
local surface_DrawText = surface.DrawText
|
||||
local surface_GetTextSize = surface.GetTextSize
|
||||
|
||||
function DEMO:DrawPrettyText(text, x, y, font, size, weight, blursize, color1, color2, align_mult_x, align_mult_y)
|
||||
align_mult_x = align_mult_x or 0
|
||||
align_mult_y = align_mult_y or 0
|
||||
font = font or "Arial"
|
||||
size = size or 14
|
||||
weight = weight or 0
|
||||
blursize = blursize or 1
|
||||
color1 = color1 or def_color1
|
||||
color2 = color2 or def_color2
|
||||
|
||||
fonts[font] = fonts[font] or {}
|
||||
fonts[font][size] = fonts[font][size] or {}
|
||||
fonts[font][size][weight] = fonts[font][size][weight] or {}
|
||||
fonts[font][size][weight][blursize] = fonts[font][size][weight][blursize] or create_fonts(font, size, weight, blursize)
|
||||
|
||||
surface_SetFont(fonts[font][size][weight][blursize].blur)
|
||||
local w, h = surface_GetTextSize(text)
|
||||
surface_SetTextColor(color2)
|
||||
|
||||
align_mult_x = (w * align_mult_x)
|
||||
align_mult_y = (h * align_mult_y)
|
||||
|
||||
for i = 1, 5 do
|
||||
surface_SetTextPos(x - align_mult_x, y - align_mult_y) -- this resets for some reason after drawing
|
||||
surface_DrawText(text)
|
||||
end
|
||||
|
||||
surface_SetFont(fonts[font][size][weight][blursize].main)
|
||||
surface_SetTextColor(color1)
|
||||
|
||||
surface_SetTextPos(x - align_mult_x, y - align_mult_y)
|
||||
surface_DrawText(text)
|
||||
|
||||
return w, h
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function DEMO:OnStart(w, h)
|
||||
|
||||
input.SetCursorPos(w/2, h/2)
|
||||
|
||||
surface.SetDrawColor(0,0,0,255)
|
||||
surface.DrawRect(0,0,w,h)
|
||||
|
||||
self.first = true
|
||||
self.cam_pos = Vector(0, 0, 0)
|
||||
self.spos = Vector(w, h) / 2
|
||||
|
||||
self.max_size = 16
|
||||
|
||||
self.max_particles = 2000
|
||||
self.particles = {}
|
||||
self.base_color = math.random(360)
|
||||
end
|
||||
|
||||
function DEMO:CreateParticle(x, y, vx, vy, life, on_death)
|
||||
life = life or math.Rand(0.25, 2)
|
||||
|
||||
|
||||
local siz = math.Rand(0.5,self.max_size)
|
||||
table.insert(
|
||||
self.particles,
|
||||
{
|
||||
pos = {x = x, y = y},
|
||||
vel = {x = vx, y = vy},
|
||||
siz = siz,
|
||||
clr = HSVToColor(math.Rand(0, 60) + self.base_color, 1, 1),
|
||||
drag = 0.99 - (siz/150) ^ 3,
|
||||
tex_id1 = table.Random(lines),
|
||||
tex_id2 = table.Random(sprites),
|
||||
on_death = on_death,
|
||||
life = self.time + life,
|
||||
random = math.Rand(-1,1),
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function DEMO:PreUpdate(w, h, t, d)
|
||||
if input.IsKeyDown(KEY_W) then
|
||||
self.cam_pos.y = self.cam_pos.y + d
|
||||
elseif input.IsKeyDown(KEY_S) then
|
||||
self.cam_pos.y = self.cam_pos.y - d
|
||||
end
|
||||
|
||||
if input.IsKeyDown(KEY_A)then
|
||||
self.cam_pos.x = self.cam_pos.x - d
|
||||
elseif input.IsKeyDown(KEY_D) then
|
||||
self.cam_pos.x = self.cam_pos.x + d
|
||||
end
|
||||
|
||||
local mat = Matrix()
|
||||
|
||||
mat:Translate(Vector(w/2,h/2,0))
|
||||
|
||||
mat:Translate(Vector(self.cam_pos.x * 100, 0, 0))
|
||||
mat:Scale(Vector(1, 1, 1) * math.min(t ^ 4, 1))
|
||||
mat:Rotate(Angle(0, 0, 0))
|
||||
|
||||
mat:Translate(-Vector(w/2,h/2,0))
|
||||
|
||||
return mat
|
||||
end
|
||||
|
||||
local ext_vel_x = 0
|
||||
local ext_vel_y = 0
|
||||
local ext_vel_z = 0
|
||||
|
||||
local blur = Material("pp/blurscreen")
|
||||
|
||||
local function blur_screen(w, h, x, y)
|
||||
surface.SetMaterial(blur)
|
||||
surface.SetDrawColor(255, 50, 50, 2)
|
||||
|
||||
for i = 0, 10 do
|
||||
blur:SetFloat("$blur", i / 10)
|
||||
blur:Recompute()
|
||||
render.UpdateScreenEffectTexture()
|
||||
surface.DrawTexturedRect(x * math.random(-16, 16), y * math.random(-16, 16), w, h)
|
||||
end
|
||||
end
|
||||
|
||||
surface.CreateFont("pace_about_1", {font = "Roboto Bold", size = 512, weight = 800, additive = false, antialias = true})
|
||||
|
||||
local credits = {}
|
||||
local A = function(str, size, ...) table.insert(credits, {str, size or isstring(str) and 1 or nil, ...}) end
|
||||
|
||||
local cast = {
|
||||
"morshmellow",
|
||||
"immortalyes",
|
||||
"kilroy",
|
||||
"white queen",
|
||||
"dizrahk",
|
||||
"kerahk",
|
||||
"Nomad'Zorah vas Source",
|
||||
"Verbal Silence",
|
||||
"Madness",
|
||||
"Techbot",
|
||||
"Elmo",
|
||||
"Arctic",
|
||||
"krionikal",
|
||||
"Gm Matsilagi",
|
||||
"Daft Lad",
|
||||
"GigiSpahz",
|
||||
"Black Tea",
|
||||
"RocketMania",
|
||||
"ssogal",
|
||||
"Expresso",
|
||||
"Ryokon!",
|
||||
"Zeriga",
|
||||
"Aeo",
|
||||
"techbot",
|
||||
"midori",
|
||||
"sauer",
|
||||
"LilTrenya",
|
||||
"maarc",
|
||||
"dekota",
|
||||
"liltrenya",
|
||||
"nanori",
|
||||
"svetlana",
|
||||
"scud",
|
||||
}
|
||||
|
||||
local koreans = {
|
||||
"black tea",
|
||||
"ssogal",
|
||||
"girong",
|
||||
"scud",
|
||||
"명박오니●",
|
||||
"rocketmania",
|
||||
"maybe",
|
||||
"lac",
|
||||
"chupa",
|
||||
"momo",
|
||||
"천령씨",
|
||||
}
|
||||
|
||||
local japanese = {
|
||||
"kilroy",
|
||||
"bubu",
|
||||
"ゆっけりあーの",
|
||||
"yomofox",
|
||||
"zaguya",
|
||||
"acchan",
|
||||
"cabin mild",
|
||||
"enngawa",
|
||||
"freeman",
|
||||
"piichan",
|
||||
"fia",
|
||||
}
|
||||
|
||||
for _, text in RandomPairs(japanese) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("00:06 - *DEAD* Bubu: おおおっきいいいいおおおおおおお", 2)
|
||||
|
||||
for _, text in RandomPairs(koreans) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("ㅋㅋㅋㅋㅋㅋㅋㅋㅋ", 2)
|
||||
|
||||
for _, text in RandomPairs(cast) do
|
||||
A(text, 1, 0)
|
||||
end
|
||||
|
||||
A("makeup department", 2)
|
||||
|
||||
A(4)
|
||||
|
||||
A("black tea", 1)
|
||||
A("momo", 1.5)
|
||||
A("yomofox", 1)
|
||||
A("translations", 2)
|
||||
|
||||
A("your imagination")
|
||||
A("garry")
|
||||
A("puush")
|
||||
A("gdrive")
|
||||
A("dropbox")
|
||||
A("metastruct")
|
||||
A("Production Management", 2)
|
||||
|
||||
A("workshop")
|
||||
A("garrysmod.org")
|
||||
A("nexusmods")
|
||||
A("valve")
|
||||
A("Art Direction", 2)
|
||||
|
||||
A("Mark James")
|
||||
A("Editor Icons", 2)
|
||||
|
||||
A("Morten")
|
||||
A("HTML Department", 2)
|
||||
A(4)
|
||||
A("capsadmin", 1)
|
||||
A("written and managed by", 1)
|
||||
A("pac3", 4)
|
||||
|
||||
local start_height = 0
|
||||
local text_size = 32
|
||||
local text_spacing = 4
|
||||
|
||||
for k,v in pairs(credits) do
|
||||
if v[2] then
|
||||
v[1] = v[1]:upper()
|
||||
start_height = start_height + text_size + text_spacing
|
||||
end
|
||||
end
|
||||
|
||||
start_height = start_height * 1.75
|
||||
|
||||
function DEMO:DrawCredits(w, h, d, t, pos)
|
||||
local last_height = 0
|
||||
|
||||
for i, data in pairs(credits) do
|
||||
if not data[2] then
|
||||
last_height = last_height + data[1] * text_size + text_spacing
|
||||
else
|
||||
local w, h = self:DrawPrettyText(
|
||||
data[1],
|
||||
self.spos.x,
|
||||
-t * 30 + self.spos.y - last_height + start_height,
|
||||
|
||||
"Roboto-Black",
|
||||
text_size * data[2],
|
||||
|
||||
0,
|
||||
10,
|
||||
|
||||
Color(255, 255, 255, 200),
|
||||
Color(255, 100, 255, 50),
|
||||
|
||||
data[3] or 0.5,
|
||||
1
|
||||
)
|
||||
|
||||
last_height = last_height + h * data[2] + text_spacing
|
||||
end
|
||||
end
|
||||
|
||||
self.spos = self.spos + ((pos - self.spos) * d)
|
||||
end
|
||||
|
||||
function DEMO:DrawParticles(w, h, d, t, pos)
|
||||
d = d * 50
|
||||
|
||||
local mult = 0.00001
|
||||
|
||||
if input.IsMouseDown(MOUSE_RIGHT) then
|
||||
mult = 0.0001
|
||||
end
|
||||
|
||||
for i, part in pairs(self.particles) do
|
||||
-- random velocity for some variation
|
||||
part.vel.x = part.vel.x + ((pos.x - part.pos.x) * mult * part.siz) + math.Rand(-0.1,0.1)
|
||||
part.vel.y = part.vel.y + ((pos.y - part.pos.y) * mult * part.siz) + math.Rand(-0.1,0.1)
|
||||
|
||||
-- velocity
|
||||
part.pos.x = part.pos.x + (part.vel.x * d)
|
||||
part.pos.y = part.pos.y + (part.vel.y * d)
|
||||
|
||||
-- friction
|
||||
part.vel.x = part.vel.x * part.drag
|
||||
part.vel.y = part.vel.y * part.drag
|
||||
|
||||
-- collision with other particles (buggy)
|
||||
if part.pos.x - part.siz < 0 then
|
||||
part.pos.x = 0 + part.siz * 1
|
||||
part.vel.x = part.vel.x * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.x + part.siz > w then
|
||||
part.pos.x = w - part.siz
|
||||
part.vel.x = part.vel.x * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.y - part.siz < 0 then
|
||||
part.pos.y = 0 + part.siz * 1
|
||||
part.vel.y = part.vel.y * -part.drag
|
||||
end
|
||||
|
||||
if part.pos.y + part.siz > h then
|
||||
part.pos.y = h + part.siz * -1
|
||||
part.vel.y = part.vel.y * -part.drag
|
||||
end
|
||||
|
||||
local l = (part.vel.x * part.vel.y) + 5
|
||||
l = l * 0.75
|
||||
|
||||
local life_scale = math.min(part.life - t, 1) ^ 2
|
||||
local s = math.min(part.siz * l + 40, 100)
|
||||
|
||||
surface.SetTexture(part.tex_id2)
|
||||
|
||||
surface.SetDrawColor(part.clr.r, part.clr.g, part.clr.b, 255)
|
||||
self:DrawLineEx(
|
||||
part.pos.x,
|
||||
part.pos.y,
|
||||
part.pos.x - part.vel.x*l,
|
||||
part.pos.y - part.vel.y*l,
|
||||
|
||||
part.siz * life_scale, true
|
||||
)
|
||||
|
||||
s = s * life_scale
|
||||
|
||||
surface.SetDrawColor(part.clr.r*0.1*l, part.clr.g*0.1*l, part.clr.b*0.1*l, 255)
|
||||
surface.DrawTexturedRect(
|
||||
(part.pos.x - s * 0.5),
|
||||
(part.pos.y - s * 0.5),
|
||||
s,
|
||||
s
|
||||
)
|
||||
|
||||
if part.life < t and (not part.on_death or part:on_death() ~= false) then
|
||||
self.particles[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DEMO:DrawPostProcess(w, h, d, t, pos)
|
||||
local params = {}
|
||||
|
||||
params["$pp_colour_addr"] = 0
|
||||
params["$pp_colour_addg"] = 0
|
||||
params["$pp_colour_addb"] = 0
|
||||
params["$pp_colour_brightness"] = -0.1
|
||||
params["$pp_colour_contrast"] = 0.8
|
||||
params["$pp_colour_colour"] = math.sin(t) * 1 - 0.5
|
||||
params["$pp_colour_mulr"] = math.sin(t) / 3
|
||||
params["$pp_colour_mulg"] = math.cos(t) / 2
|
||||
params["$pp_colour_mulb"] = math.asin(t) / 2
|
||||
DrawColorModify(params)
|
||||
|
||||
local vel = ((self.last_pos or pos) - pos):Length() / 200
|
||||
|
||||
if vel > 1 then
|
||||
self.cursor = "arrow"
|
||||
else
|
||||
self.cursor = "none"
|
||||
end
|
||||
|
||||
vel = vel + 0.1
|
||||
|
||||
DrawSunbeams(0.5, vel, 0.05, self.spos.x / w, self.spos.y / h)
|
||||
blur_screen(w, h, self.spos.x / w, self.spos.y / h)
|
||||
end
|
||||
|
||||
local function ang_to_dir(ang, scale)
|
||||
ang = math.deg(ang)
|
||||
scale = scale or 1
|
||||
return math.sin(ang) * scale, math.cos(ang) * scale
|
||||
end
|
||||
|
||||
function DEMO:SpawnFireworks(x, y)
|
||||
local vx, vy = ang_to_dir(math.Rand(-45, 45), math.Rand(10, 20))
|
||||
self:CreateParticle(x, y, vx, vy, nil, function(part)
|
||||
for i = -90, 90 do
|
||||
self:CreateParticle(part.pos.x, part.pos.y, ang_to_dir(i * 2, math.Rand(1, 5) * math.Rand(1, 2)))
|
||||
end
|
||||
|
||||
self.base_color = self.base_color + math.Rand(30, 60)
|
||||
end)
|
||||
end
|
||||
|
||||
function DEMO:OnDraw(w, h, d, t, pos)
|
||||
|
||||
-- background
|
||||
surface.SetDrawColor(0, 0, 0, 20)
|
||||
surface.DrawRect(w*-1, h*-1, w*4, h*4)
|
||||
|
||||
if input.IsMouseDown(MOUSE_LEFT) then
|
||||
self:SpawnFireworks(input.GetCursorPos())
|
||||
end
|
||||
|
||||
if math.random() > 0.99 then
|
||||
self:SpawnFireworks(math.Rand(0, w), h - 20)
|
||||
end
|
||||
|
||||
self:DrawCredits(w, h, d, t, pos)
|
||||
self:DrawParticles(w, h, d, t, pos)
|
||||
self:DrawPostProcess(w, h, d, t, pos)
|
||||
|
||||
self.last_pos = pos
|
||||
end
|
||||
|
||||
function DEMO:OnUpate(w, h, d, t, pos, first)
|
||||
self.time = t
|
||||
|
||||
if first then
|
||||
local ok, err = pcall(self.OnStart, self, w, h)
|
||||
if not ok then return ok, err end
|
||||
end
|
||||
|
||||
local ok, mat = pcall(self.PreUpdate, self, w, h, t, d)
|
||||
|
||||
if not ok then return ok, mat end
|
||||
|
||||
cam.Start2D()
|
||||
if mat then cam.PushModelMatrix(mat) end
|
||||
local ok, err = pcall(self.OnDraw, self, w, h, d, t, pos)
|
||||
if mat then cam.PopModelMatrix() end
|
||||
cam.End2D()
|
||||
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function pace.ShowAbout()
|
||||
|
||||
local pnl = vgui.Create("Panel")
|
||||
pnl:SetPos(0, 0)
|
||||
pnl:SetSize(ScrW(), ScrH())
|
||||
pnl:MakePopup()
|
||||
|
||||
local html = vgui.Create("DHTML", pnl)
|
||||
html:OpenURL("https://www.youtube.com/watch?v=Kvg7oTfGhYg")
|
||||
|
||||
local first = true
|
||||
local start_time = RealTime()
|
||||
|
||||
pac.AddHook("PreRender", "pace_about", function()
|
||||
|
||||
local w, h = ScrW(), ScrH()
|
||||
local t = RealTime() - start_time
|
||||
local d = FrameTime()
|
||||
|
||||
local ok, err = DEMO:OnUpate(w, h, d, t, Vector(input.GetCursorPos()), first)
|
||||
|
||||
if pnl.last_cursor ~= DEMO.cursor then
|
||||
pnl:SetCursor(DEMO.cursor or "arrow")
|
||||
pnl.last_cursor = DEMO.cursor
|
||||
end
|
||||
|
||||
first = false
|
||||
|
||||
local quit = input.IsKeyDown(KEY_SPACE) or input.IsKeyDown(KEY_ESCAPE) or not ok
|
||||
|
||||
if quit then
|
||||
if not ok then print(err) end
|
||||
pnl:Remove()
|
||||
pac.RemoveHook("PreRender", "pace_about")
|
||||
return
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
1142
lua/pac3/editor/client/animation_timeline.lua
Normal file
1142
lua/pac3/editor/client/animation_timeline.lua
Normal file
File diff suppressed because it is too large
Load Diff
1524
lua/pac3/editor/client/asset_browser.lua
Normal file
1524
lua/pac3/editor/client/asset_browser.lua
Normal file
File diff suppressed because it is too large
Load Diff
7212
lua/pac3/editor/client/examples.lua
Normal file
7212
lua/pac3/editor/client/examples.lua
Normal file
File diff suppressed because one or more lines are too long
74
lua/pac3/editor/client/fonts.lua
Normal file
74
lua/pac3/editor/client/fonts.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
--[[
|
||||
| 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 L = pace.LanguageString
|
||||
|
||||
pace.Fonts = {}
|
||||
|
||||
for i = 1, 5 do
|
||||
surface.CreateFont("pac_font_"..i,
|
||||
{
|
||||
font = "Arial",
|
||||
size = 11 + i,
|
||||
weight = 50,
|
||||
antialias = true,
|
||||
})
|
||||
|
||||
table.insert(pace.Fonts, "pac_font_"..i)
|
||||
end
|
||||
|
||||
for i = 1, 5 do
|
||||
surface.CreateFont("pac_font_bold"..i,
|
||||
{
|
||||
font = "Arial",
|
||||
size = 11 + i,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
})
|
||||
table.insert(pace.Fonts, "pac_font_bold"..i)
|
||||
end
|
||||
|
||||
table.insert(pace.Fonts, "DermaDefault")
|
||||
table.insert(pace.Fonts, "DermaDefaultBold")
|
||||
|
||||
local font_cvar = CreateClientConVar("pac_editor_font", pace.Fonts[1])
|
||||
|
||||
function pace.SetFont(fnt)
|
||||
pace.CurrentFont = fnt or font_cvar:GetString()
|
||||
|
||||
if not table.HasValue(pace.Fonts, pace.CurrentFont) then
|
||||
pace.CurrentFont = "DermaDefault"
|
||||
end
|
||||
|
||||
RunConsoleCommand("pac_editor_font", pace.CurrentFont)
|
||||
|
||||
if pace.Editor and pace.Editor:IsValid() then
|
||||
pace.CloseEditor()
|
||||
timer.Simple(0.1, function()
|
||||
pace.OpenEditor()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function pace.AddFontsToMenu(menu)
|
||||
local menu,pnl = menu:AddSubMenu(L"font")
|
||||
pnl:SetImage("icon16/text_bold.png")
|
||||
menu.GetDeleteSelf = function() return false end
|
||||
|
||||
for key, val in pairs(pace.Fonts) do
|
||||
local pnl = menu:AddOption(L"The quick brown fox jumps over the lazy dog. (" ..val ..")", function()
|
||||
pace.SetFont(val)
|
||||
end)
|
||||
|
||||
pnl:SetFont(val)
|
||||
end
|
||||
end
|
||||
|
||||
pace.SetFont()
|
||||
49
lua/pac3/editor/client/icons.lua
Normal file
49
lua/pac3/editor/client/icons.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
pace.MiscIcons = {
|
||||
about = "icon16/star.png",
|
||||
appearance = "icon16/paintcan.png",
|
||||
autoload = "icon16/transmit_go.png",
|
||||
chat = "icon16/comment.png",
|
||||
clear = "icon16/cross.png",
|
||||
clone = "icon16/page_copy.png",
|
||||
copy = "icon16/page_white_text.png",
|
||||
edit = "icon16/table_edit.png",
|
||||
error = "icon16/exclamation.png",
|
||||
exit = "icon16/cancel.png",
|
||||
font = "icon16/text_smallcaps.png",
|
||||
help = "icon16/help.png",
|
||||
info = "icon16/information.png",
|
||||
language = "icon16/user_comment.png",
|
||||
load = "icon16/folder.png",
|
||||
new = "icon16/add.png",
|
||||
orientation = "icon16/shape_handles.png",
|
||||
outfit = "icon16/group.png",
|
||||
paste = "icon16/paste_plain.png",
|
||||
replace = "icon16/arrow_refresh.png",
|
||||
revert = "icon16/table_delete.png",
|
||||
save = "icon16/disk.png",
|
||||
uniqueid = "icon16/vcard.png",
|
||||
url = "icon16/server_go.png",
|
||||
warning = "icon16/error.png",
|
||||
wear = "icon16/transmit.png",
|
||||
}
|
||||
|
||||
pace.GroupsIcons = {
|
||||
effects = 'icon16/wand.png',
|
||||
model = 'icon16/shape_square.png',
|
||||
entity = 'icon16/brick.png',
|
||||
modifiers = 'icon16/disconnect.png',
|
||||
advanced = 'icon16/page_white_gear.png',
|
||||
experimental = 'icon16/bug.png',
|
||||
legacy = 'icon16/hourglass.png',
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user