This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 0e770b2b49
commit 94063e4369
7342 changed files with 1718932 additions and 14 deletions

View 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()

View 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()

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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)

View 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)

View 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)

View 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)

View 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

View 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

View 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

View 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

View 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()

View 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()

View 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)

View 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)

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

File diff suppressed because it is too large Load Diff

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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

View 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")

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

File diff suppressed because it is too large Load Diff

View 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)

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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)

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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)

View 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)

View 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)

View 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")

View 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

View 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)

View 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

View 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()

View 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)

View 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)

View 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)

View 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

View 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

View 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

View 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

View 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)

View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View 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()

View 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