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