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