--[[ | 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 = "proxy" PART.ThinkTime = 0 PART.Group = 'modifiers' PART.Icon = 'icon16/calculator.png' BUILDER:StartStorableVars() BUILDER:SetPropertyGroup("generic") BUILDER:GetSet("VariableName", "", {enums = function(part) local part = part:GetTarget() if not part:IsValid() then return end local tbl = {} for _, info in pairs(part:GetProperties()) do if info.key == "UniqueID" then goto CONTINUE end local T = type(info.get()) if T == "number" or T == "Vector" or T == "Angle" or T == "boolean" then tbl[info.key] = info.key end ::CONTINUE:: end return tbl end}) BUILDER:GetSet("RootOwner", false) BUILDER:GetSetPart("TargetPart") BUILDER:GetSetPart("OutputTargetPart", {hide_in_editor = true}) BUILDER:GetSet("AffectChildren", false) BUILDER:GetSet("Expression", "") BUILDER:SetPropertyGroup("easy setup") BUILDER:GetSet("Input", "time", {enums = function(part) return part.Inputs end}) BUILDER:GetSet("Function", "sin", {enums = function(part) return part.Functions end}) BUILDER:GetSet("Axis", "") BUILDER:GetSet("Min", 0) BUILDER:GetSet("Max", 1) BUILDER:GetSet("Offset", 0) BUILDER:GetSet("InputMultiplier", 1) BUILDER:GetSet("InputDivider", 1) BUILDER:GetSet("Pow", 1) BUILDER:SetPropertyGroup("behavior") BUILDER:GetSet("Additive", false) BUILDER:GetSet("PlayerAngles", false) BUILDER:GetSet("ZeroEyePitch", false) BUILDER:GetSet("ResetVelocitiesOnHide", true) BUILDER:GetSet("VelocityRoughness", 10) BUILDER:EndStorableVars() -- redirect function PART:SetOutputTargetPart(part) if not part:IsValid() then return end self.SetOutputTargetPartUID = "" self:SetTargetPart(part) end function PART:GetPhysicalTarget() local part = self:GetTargetPart() if part:IsValid() then return part end local parent = self:GetParent() while not parent.GetWorldPosition or parent:GetWorldPosition():IsZero() do if not parent.Parent:IsValid() then break end parent = parent.Parent end if not parent.GetWorldPosition then local owner = parent:GetOwner() if owner:IsValid() then return owner end end return parent end function PART:GetTarget() local part = self:GetTargetPart() if part:IsValid() then return part end return self:GetParent() end function PART:SetVariableName(str) self.VariableName = str end function PART:GetNiceName() if self:GetVariableName() == "" then return self.ClassName end local target if self.AffectChildren then target = "children" else local part = self:GetTarget() if part:IsValid() then target = part:GetName() end end local axis = self:GetAxis() if axis ~= "" then axis = "." .. axis end return (target or "?") .. "." .. pac.PrettifyName(self:GetVariableName()) .. axis .. " = " .. (self.debug_var or "?") end function PART:Initialize() self.vec_additive = {} self.next_vel_calc = 0 end PART.Functions = { none = function(n) return n end, sin = math.sin, cos = math.cos, tan = math.tan, abs = math.abs, mod = function(n, s) return n%s.Max end, sgn = function(n) return n>0 and 1 or n<0 and -1 or 0 end, } local FrameTime = FrameTime function PART:CalcVelocity() self.last_vel = self.last_vel or Vector() self.last_vel_smooth = self.last_vel_smooth or self.last_vel or Vector() self.last_vel_smooth = (self.last_vel_smooth + (self.last_vel - self.last_vel_smooth) * FrameTime() * math.max(self.VelocityRoughness, 0.1)) end function PART:GetVelocity(part) local pos if part.GetWorldPosition then pos = part:GetWorldPosition() else if IsEntity(part) then pos = part:GetPos() elseif part:GetOwner():IsValid() then pos = part:GetOwner():GetPos() end end local time = pac.RealTime if self.next_vel_calc < time then self.next_vel_calc = time + 0.1 self.last_vel = (self.last_pos or pos) - pos self.last_pos = pos end return self.last_vel_smooth / 5 end function PART:CalcEyeAngles(ent) local ang = self.PlayerAngles and ent:GetAngles() or ent:EyeAngles() if self.ZeroEyePitch then ang.p = 0 end return ang end local function try_viewmodel(ent) return ent == pac.LocalViewModel and pac.LocalPlayer or ent end local function get_owner(self) if self.RootOwner then return try_viewmodel(self:GetRootPart():GetOwner()) else return try_viewmodel(self:GetOwner()) end end PART.Inputs = {} PART.Inputs.property = function(self, property_name, field) local part = self.TargetEntity:IsValid() and self.TargetEntity or self:GetParent() if part:IsValid() and part.GetProperty and property_name then local v = part:GetProperty(property_name) local T = type(v) if T == "Vector" or T == "Angle" then if field and v[field] then return v[field] else return v[1],v[2],v[3] end elseif T == "boolean" then return v and 1 or 0 elseif T == "number" then return v end end return 0 end PART.Inputs.owner_position = function(self) local owner = get_owner(self) if owner:IsValid() then local pos = owner:GetPos() return pos.x, pos.y, pos.z end return 0,0,0 end PART.Inputs.owner_position_x = function(self) local owner = get_owner(self) if owner:IsValid() then local pos = owner:GetPos() return pos.x end return 0 end PART.Inputs.owner_position_y = function(self) local owner = get_owner(self) if owner:IsValid() then local pos = owner:GetPos() return pos.y end return 0 end PART.Inputs.owner_position_z = function(self) local owner = get_owner(self) if owner:IsValid() then local pos = owner:GetPos() return pos.z end return 0 end PART.Inputs.owner_fov = function(self) local owner = get_owner(self) if owner:IsValid() and owner.GetFOV then return owner:GetFOV() end return 0 end PART.Inputs.visible = function(self, radius) local part = self:GetPhysicalTarget() if not part.GetWorldPosition then return 0 end part.proxy_pixvis = part.proxy_pixvis or util.GetPixelVisibleHandle() return util.PixelVisible(part:GetWorldPosition(), radius or 16, part.proxy_pixvis) or 0 end PART.Inputs.time = RealTime PART.Inputs.synced_time = CurTime PART.Inputs.systime = SysTime PART.Inputs.stime = SysTime PART.Inputs.frametime = FrameTime PART.Inputs.ftime = FrameTime PART.Inputs.framenumber = FrameNumber PART.Inputs.fnumber = FrameNumber PART.Inputs.random = function(self, min, max) min = min or 0 max = max or 1 return min + math.random()*(max-min) end PART.Inputs.random_once = function(self, seed, min, max) min = min or 0 max = max or 1 seed = seed or 0 self.rand_id = self.rand_id or {} if seed then self.rand_id[seed] = self.rand_id[seed] or min + math.random()*(max-min) else self.rand = self.rand or min + math.random()*(max-min) end return self.rand_id[seed] or self.rand end PART.Inputs.lerp = function(self, m, a, b) m = tonumber(m) or 0 a = tonumber(a) or -1 b = tonumber(b) or 1 return (b - a) * m + a end for ease,f in pairs(math.ease) do if string.find(ease,"In") or string.find(ease,"Out") then local f2 = function(self, frac, min, max) min = min or 0 max = max or 1 return min + f(frac)*(max-min) end PART.Inputs["ease"..ease] = f2 PART.Inputs["ease_"..ease] = f2 PART.Inputs[ease] = f2 end end PART.Inputs.timeex = function(s) s.time = s.time or pac.RealTime return pac.RealTime - s.time end PART.Inputs.part_distance = function(self, uid1, uid2) if not uid1 or not uid2 then return 0 end local PartA = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), uid1) if not PartA:IsValid() then PartA = pac.FindPartByName(pac.Hash(pac.LocalPlayer), uid1, self) end local PartB = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), uid2) if not PartB:IsValid() then PartB = pac.FindPartByName(pac.Hash(pac.LocalPlayer), uid2, self) end if not PartA:IsValid() or not PartB:IsValid() then return 0 end return (PartB:GetWorldPosition() - PartA:GetWorldPosition()):Length() end PART.Inputs.event_alternative = function(self, uid1, num1, num2) if not uid1 then return 0 end local PartA = pac.GetPartFromUniqueID(pac.Hash(pac.LocalPlayer), uid1) if not PartA:IsValid() then PartA = pac.FindPartByName(pac.Hash(pac.LocalPlayer), uid1, self) end if PartA.ClassName == "event" then if PartA.event_triggered then return num1 or 0 else return num2 or 0 end else return -1 end return 0 end PART.Inputs.number_operator_alternative = function(self, comp1, op, comp2, num1, num2) if not (comp1 and op and comp2 and num1 and num2) then return -1 end if not (isnumber(comp1) and isnumber(comp2) and isnumber(num1) and isnumber(num2)) then return -1 end local b = true if op == "=" or op == "==" or op == "equal" then b = comp1 == comp2 elseif op == ">" or op == "above" or op == "greater" or op == "greater than" then b = comp1 > comp2 elseif op == ">=" or op == "above or equal" or op == "greater or equal" or op == "greater than or equal" then b = comp1 >= comp2 elseif op == "<" or op == "below" or op == "less" or op == "less than" then b = comp1 < comp2 elseif op == "<=" or op == "below or equal" or op == "less or equal" or op == "less than or equal" then b = comp1 <= comp2 elseif op == "~=" or op == "~=" or op == "not equal" then b = comp1 ~= comp2 end if b then return num1 or 0 else return num2 or 0 end end do local function get_pos(self) local part = self:GetPhysicalTarget() if not part:IsValid() then return end local pos if part.GetWorldPosition then pos = part:GetWorldPosition() else local owner = get_owner(part) if not owner:IsValid() then return end pos = owner:GetPos() end return pos end PART.Inputs.eye_position_distance = function(self) local pos = get_pos(self) if not pos then return 0 end return pos:Distance(pac.EyePos) end PART.Inputs.eye_angle_distance = function(self) local pos = get_pos(self) if not pos then return 0 end return math.Clamp(math.abs(pac.EyeAng:Forward():DotProduct((pos - pac.EyePos):GetNormalized())) - 0.5, 0, 1) end end PART.Inputs.aim_length = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local res = util.QuickTrace(owner:EyePos(), self:CalcEyeAngles(owner):Forward() * 16000, {owner, owner:GetParent()}) return res.StartPos:Distance(res.HitPos) end PART.Inputs.aim_length_fraction = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local res = util.QuickTrace(owner:EyePos(), self:CalcEyeAngles(owner):Forward() * 16000, {owner, owner:GetParent()}) return res.Fraction end do local function get_eye_angle(self, field) local owner = get_owner(self) if not owner:IsValid() then return 0 end local n = self:CalcEyeAngles(owner)[field] if field == "p" then return -(1 + math.NormalizeAngle(n) / 89) / 2 + 1 end return math.NormalizeAngle(n)/90 end PART.Inputs.owner_eye_angle_pitch = function(self) return get_eye_angle(self, "p") end PART.Inputs.owner_eye_angle_yaw = function(self) return get_eye_angle(self, "y") end PART.Inputs.owner_eye_angle_roll = function(self) return get_eye_angle(self, "r") end end do local function get_scale(self, field) local owner = get_owner(self) if not owner:IsValid() then return 1 end return owner.pac_model_scale and owner.pac_model_scale[field] or (owner.GetModelScale and owner:GetModelScale()) or 1 end PART.Inputs.owner_scale_x = function(self) return get_scale(self, "x") end PART.Inputs.owner_scale_y = function(self) return get_scale(self, "y") end PART.Inputs.owner_scale_z = function(self) return get_scale(self, "z") end end -- outfit owner PART.Inputs.owner_velocity_length = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end return self:GetVelocity(owner):Length() end do local function get_velocity(self, field) local owner = get_owner(self) if not owner:IsValid() then return 0 end local dir = self:CalcEyeAngles(owner) return dir[field](dir):Dot(self:GetVelocity(owner)) end PART.Inputs.owner_velocity_forward = function(self) return get_velocity(self, "Forward") end PART.Inputs.owner_velocity_right = function(self) return get_velocity(self, "Right") end PART.Inputs.owner_velocity_up = function(self) return get_velocity(self, "Up") end end do -- velocity world local function get_velocity(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end return self:GetVelocity(owner) end PART.Inputs.owner_velocity_world_forward = function(self) return get_velocity(self)[1] end PART.Inputs.owner_velocity_world_right = function(self) return get_velocity(self)[2] end PART.Inputs.owner_velocity_world_up = function(self) return get_velocity(self)[3] end end -- outfit owner vel increase PART.Inputs.owner_velocity_length_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:GetVelocity(owner):Length() self.ov_length_i = (self.ov_length_i or 0) + vel * FrameTime() return self.ov_length_i end do PART.Inputs.owner_velocity_forward_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:CalcEyeAngles(owner):Forward():Dot(self:GetVelocity(owner)) self.ov_forward_i = (self.ov_forward_i or 0) + vel * FrameTime() return self.ov_forward_i end PART.Inputs.owner_velocity_right_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:CalcEyeAngles(owner):Right():Dot(self:GetVelocity(owner)) self.ov_right_i = (self.ov_right_i or 0) + vel * FrameTime() return self.ov_right_i end PART.Inputs.owner_velocity_up_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:CalcEyeAngles(owner):Up():Dot(self:GetVelocity(owner)) self.ov_up_i = (self.ov_up_i or 0) + vel * FrameTime() return self.ov_up_i end end do -- PART.Inputs.owner_velocity_world_forward_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:GetVelocity(owner)[1] self.ov_wforward_i = (self.ov_wforward_i or 0) + vel * FrameTime() return self.ov_wforward_i end PART.Inputs.owner_velocity_world_right_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:GetVelocity(owner)[2] self.ov_wright_i = (self.ov_wright_i or 0) + vel * FrameTime() return self.ov_wright_i end PART.Inputs.owner_velocity_world_up_increase = function(self) local owner = get_owner(self) if not owner:IsValid() then return 0 end local vel = self:GetVelocity(owner)[3] self.ov_wup_i = (self.ov_wup_i or 0) + vel * FrameTime() return self.ov_wup_i end end do -- velocity PART.Inputs.parent_velocity_length = function(self) return self:GetVelocity(self:GetPhysicalTarget()):Length() end local function get_velocity(self) local part = self:GetPhysicalTarget() local ang if part.GetWorldAngles then ang = part:GetWorldAngles() elseif part.GetAngles then ang = part:GetAngles() end return ang and self:GetVelocity(part), ang end PART.Inputs.parent_velocity_forward = function(self) local vel, ang = get_velocity(self) if not vel then return 0 end return -ang:Forward():Dot(vel) end PART.Inputs.parent_velocity_right = function(self) local vel, ang = get_velocity(self) if not vel then return 0 end return ang:Right():Dot(vel) end PART.Inputs.parent_velocity_up = function(self) local vel, ang = get_velocity(self) if not vel then return 0 end return ang:Up():Dot(vel) end end do -- scale local function get_scale(self, field) local part = self:GetPhysicalTarget() if not part:IsValid() then return 1 end return part.Scale and part.Scale[field]*part.Size or 1 end PART.Inputs.parent_scale_x = function(self) return get_scale(self, "x") end PART.Inputs.parent_scale_y = function(self) return get_scale(self, "y") end PART.Inputs.parent_scale_z = function(self) return get_scale(self, "z") end end PART.Inputs.pose_parameter = function(self, name) if not name then return 0 end local owner = get_owner(self) if owner:IsValid() and owner.GetPoseParameter then return owner:GetPoseParameter(name) end return 0 end PART.Inputs.pose_parameter_true = function(self, name) if not name then return 0 end local owner = get_owner(self) if owner:IsValid() then local min, max = owner:GetPoseParameterRange(owner:LookupPoseParameter(name)) return min + (max - min)*(owner:GetPoseParameter(name)) end return 0 end PART.Inputs.command = function(self, name) local ply = self:GetPlayerOwner() if ply.pac_proxy_events then local data if not name then data = ply.pac_proxy_events[self.Name] else data = ply.pac_proxy_events[name] end if data then data.x = data.x or 0 data.y = data.y or 0 data.z = data.z or 0 return data.x, data.y, data.z end end return 0, 0, 0 end PART.Inputs.voice_volume = function(self) local ply = self:GetPlayerOwner() if not IsValid(ply) then return 0 end return ply:VoiceVolume() end PART.Inputs.voice_volume_scale = function(self) local ply = self:GetPlayerOwner() return ply:GetVoiceVolumeScale() end do -- light amount local ColorToHSV = ColorToHSV local render = render local function get_color(self, field) local part = self:GetPhysicalTarget() if not part:IsValid() then return 0 end if not part.GetWorldPosition then return 0 end local v = field and render.GetLightColor(part:GetWorldPosition()):ToColor()[field] or render.GetLightColor(part:GetWorldPosition()):ToColor() if part.ProperColorRange then if field then return v / 255 else return v['r']/255, v['g']/255, v['b']/255 end end if field then return v else return v['r'], v['g'], v['b'] end end PART.Inputs.light_amount = function(self) return get_color(self) end PART.Inputs.light_amount_r = function(self) return get_color(self, "r") end PART.Inputs.light_amount_g = function(self) return get_color(self, "g") end PART.Inputs.light_amount_b = function(self) return get_color(self, "b") end PART.Inputs.light_value = function(self) local part = self:GetPhysicalTarget() if not part:IsValid() then return 0 end if not part.GetWorldPosition then return 0 end local h, s, v = ColorToHSV(render.GetLightColor(part:GetWorldPosition()):ToColor()) return v end end do -- ambient light local render = render local function get_color(self, field) local part = self:GetTarget() if not part:IsValid() then return 0 end local v = field and render.GetAmbientLightColor():ToColor()[field] or render.GetAmbientLightColor():ToColor() if part.ProperColorRange then if field then return v / 255 else return v['r']/255, v['g']/255, v['b']/255 end end if field then return v else return v['r'], v['g'], v['b'] end end PART.Inputs.ambient_light = function(self) return get_color(self) end PART.Inputs.ambient_light_r = function(self) return get_color(self, "r") end PART.Inputs.ambient_light_g = function(self) return get_color(self, "g") end PART.Inputs.ambient_light_b = function(self) return get_color(self, "b") end end do -- health and armor PART.Inputs.owner_health = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:Health() end PART.Inputs.owner_max_health = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:GetMaxHealth() end PART.Inputs.owner_health_fraction = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:Health() / owner:GetMaxHealth() end PART.Inputs.owner_armor = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:Armor() end PART.Inputs.owner_max_armor = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:GetMaxArmor() end PART.Inputs.owner_armor_fraction = function(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return 0 end return owner:Armor() / owner:GetMaxArmor() end end do -- weapon and player color local Color = Color local function get_color(self, get, field) local color = field and get(self)[field] or get(self) local part = self:GetTarget() if part.ProperColorRange then if field then return color else return color[1], color[2], color[3] end end if field then return color*255 else return color[1]*255, color[2]*255, color[3]*255 end end do local function get_player_color(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return Vector(1,1,1) end return owner:GetPlayerColor() end PART.Inputs.player_color = function(self) return get_color(self, get_player_color) end PART.Inputs.player_color_r = function(self) return get_color(self, get_player_color, "r") end PART.Inputs.player_color_g = function(self) return get_color(self, get_player_color, "g") end PART.Inputs.player_color_b = function(self) return get_color(self, get_player_color, "b") end end do local function get_weapon_color(self) local owner = self:GetPlayerOwner() if not owner:IsValid() then return Vector(1,1,1) end return owner:GetWeaponColor() end PART.Inputs.weapon_color = function(self) return get_color(self, get_weapon_color) end PART.Inputs.weapon_color_r = function(self) return get_color(self, get_weapon_color, "r") end PART.Inputs.weapon_color_g = function(self) return get_color(self, get_weapon_color, "g") end PART.Inputs.weapon_color_b = function(self) return get_color(self, get_weapon_color, "b") end end end do -- ammo local function get_weapon(self) local owner = self:GetRootPart():GetOwner() if not owner:IsValid() then return 0 end if owner.GetActiveWeapon and not owner:IsWeapon() then owner = self:GetPlayerOwner() end if not owner:IsValid() then return 0 end return owner.GetActiveWeapon and owner:GetActiveWeapon() or owner, owner end PART.Inputs.owner_total_ammo = function(self, id) local owner = self:GetPlayerOwner() id = id and id:lower() if not owner:IsValid() then return 0 end return (owner.GetAmmoCount and id) and owner:GetAmmoCount(id) or 0 end PART.Inputs.weapon_primary_ammo = function(self) local wep = get_weapon(self) return wep:IsValid() and wep.Clip1 and wep:Clip1() or 0 end PART.Inputs.weapon_primary_total_ammo = function(self) local wep, owner = get_weapon(self) return wep:IsValid() and (wep.GetPrimaryAmmoType and owner.GetAmmoCount) and owner:GetAmmoCount(wep:GetPrimaryAmmoType()) or 0 end PART.Inputs.weapon_primary_clipsize = function(self) local wep = get_weapon(self) return wep:IsValid() and wep.GetMaxClip1 and wep:GetMaxClip1() or 0 end PART.Inputs.weapon_secondary_ammo = function(self) local wep = get_weapon(self) return wep:IsValid() and wep.Clip2 and wep:Clip2() or 0 end PART.Inputs.weapon_secondary_total_ammo = function(self) local wep, owner = get_weapon(self) return wep:IsValid() and (wep.GetSecondaryAmmoType and owner.GetAmmoCount) and owner:GetAmmoCount(wep:GetSecondaryAmmoType()) or 0 end PART.Inputs.weapon_secondary_clipsize = function(self) local wep = get_weapon(self) return wep:IsValid() and wep.GetMaxClip2 and wep:GetMaxClip2() or 0 end end PART.Inputs.hsv_to_color = function(self, h, s, v) local part = self:GetTarget() if not part:IsValid() then return end h = tonumber(h) or 0 s = tonumber(s) or 1 v = tonumber(v) or 1 local c = HSVToColor(h%360, s, v) if part.ProperColorRange then return c.r/255, c.g/255, c.b/255 end return c.r, c.g, c.b end do PART.Inputs.feedback = function(self) if not self.feedback then return 0 end return self.feedback[1] or 0 end PART.Inputs.feedback_x = function(self) if not self.feedback then return 0 end return self.feedback[1] or 0 end PART.Inputs.feedback_y = function(self) if not self.feedback then return 0 end return self.feedback[2] or 0 end PART.Inputs.feedback_z = function(self) if not self.feedback then return 0 end return self.feedback[3] or 0 end end PART.Inputs.flat_dot_forward = function(self) local part = get_owner(self) if part:IsValid() then local ang = part:IsPlayer() and part:EyeAngles() or part:GetAngles() ang.p = 0 ang.r = 0 local dir = pac.EyePos - part:EyePos() dir[3] = 0 dir:Normalize() return dir:Dot(ang:Forward()) end return 0 end PART.Inputs.flat_dot_right = function(self) local part = get_owner(self) if part:IsValid() then local ang = part:IsPlayer() and part:EyeAngles() or part:GetAngles() ang.p = 0 ang.r = 0 local dir = pac.EyePos - part:EyePos() dir[3] = 0 dir:Normalize() return dir:Dot(ang:Right()) end return 0 end net.Receive("pac_proxy", function() local ply = net.ReadEntity() local str = net.ReadString() local x = net.ReadFloat() local y = net.ReadFloat() local z = net.ReadFloat() if ply:IsValid() then ply.pac_proxy_events = ply.pac_proxy_events or {} ply.pac_proxy_events[str] = {name = str, x = x, y = y, z = z} end end) function PART:CheckLastVar(part) if self.last_var ~= self.VariableName then if self.last_var then part:SetProperty(self.VariableName, self.last_var_val) end self.last_var = self.VariableName self.last_var_val = part:GetProperty(self.VariableName) end end local allowed = { number = true, Vector = true, Angle = true, boolean = true, } function PART:SetExpression(str) self.Expression = str self.ExpressionFunc = nil if str and str ~= "" then local lib = {} for name, func in pairs(PART.Inputs) do lib[name] = function(...) return func(self, ...) end end local ok, res = pac.CompileExpression(str, lib) if ok then self.ExpressionFunc = res self.ExpressionError = nil self:SetError() else self.ExpressionFunc = true self.ExpressionError = res self:SetError(res) end end end function PART:OnHide() self.time = nil self.rand = nil self.rand_id = nil self.vec_additive = Vector() if self.ResetVelocitiesOnHide then self.last_vel = nil self.last_pos = nil self.last_vel_smooth = nil end if self.VariableName == "Hide" then -- cleanup event triggers on hide local part = self:GetTarget() if self.AffectChildren then for _, part in ipairs(self:GetChildren()) do part:SetEventTrigger(self, false) part.proxy_hide = nil end elseif part:IsValid() then part:SetEventTrigger(self, false) part.proxy_hide = nil end end end function PART:OnShow() self.time = nil self.rand = nil self.rand_id = nil self.vec_additive = Vector() end local function set(self, part, x, y, z, children) local val = part:GetProperty(self.VariableName) local T = type(val) if allowed[T] then if T == "boolean" then x = x or val == true and 1 or 0 local b = tonumber(x) > 0 -- special case for hide to make it behave like events if self.VariableName == "Hide" then if part.proxy_hide ~= b then -- in case parts start as hidden if b == false then part:SetKeyValueRecursive("Hide", b) end -- we want any nested proxies to think twice before they decide to enable themselves part:CallRecursiveOnClassName("proxy", "OnThink") part:SetEventTrigger(self, b) part.proxy_hide = b end -- don't apply anything to children return else part:SetProperty(self.VariableName, b) end elseif T == "number" then x = x or val part:SetProperty(self.VariableName, tonumber(x) or 0) else if self.Axis ~= "" and val[self.Axis] then val = val * 1 val[self.Axis] = x else if T == "Angle" then val = val * 1 val.p = x or val.p val.y = y or val.y val.r = z or val.r elseif T == "Vector" then val = val * 1 val.x = x or val.x val.y = y or val.y val.z = z or val.z end end part:SetProperty(self.VariableName, val) end end if children then for _, part in ipairs(part:GetChildren()) do set(self, part, x, y, z, true) end end end function PART:RunExpression(ExpressionFunc) if ExpressionFunc==true then return false,self.ExpressionError end return pcall(ExpressionFunc) end function PART:OnThink() local part = self:GetTarget() if not part:IsValid() then return end if part.ClassName == 'woohoo' then return end self:CalcVelocity() local ExpressionFunc = self.ExpressionFunc if not ExpressionFunc then self:SetExpression(self.Expression) ExpressionFunc = self.ExpressionFunc end if ExpressionFunc then local ok, x,y,z = self:RunExpression(ExpressionFunc) if not ok then if self:GetPlayerOwner() == pac.LocalPlayer and self.Expression ~= self.LastBadExpression then chat.AddText(Color(255,180,180),"============\n[ERR] PAC Proxy error on "..tostring(self)..":\n"..x.."\n============\n") self.LastBadExpression = self.Expression end return end if x and not isnumber(x) then x = 0 end if y and not isnumber(y) then y = 0 end if z and not isnumber(z) then z = 0 end if self.Additive then if x then self.vec_additive[1] = (self.vec_additive[1] or 0) + x x = self.vec_additive[1] end if y then self.vec_additive[2] = (self.vec_additive[2] or 0) + y y = self.vec_additive[2] end if z then self.vec_additive[3] = (self.vec_additive[3] or 0) + z z = self.vec_additive[3] end end self.feedback = self.feedback or {} self.feedback[1] = x self.feedback[2] = y self.feedback[3] = z if self.AffectChildren then for _, part in ipairs(self:GetChildren()) do set(self, part, x, y, z, true) end else set(self, part, x, y, z) end if pace and pace.IsActive() then local str = "" if x then str = str .. math.Round(x, 3) end if y then str = str .. ", " .. math.Round(y, 3) end if z then str = str .. ", " .. math.Round(z, 3) end local val = part:GetProperty(self.VariableName) local T = type(val) if T == "boolean" then str = tonumber(x) > 0 and "true" or "false" elseif T == "Vector" then str = "Vector(" .. str .. ")" elseif T == "Angle" then str = "Angle(" .. str .. ")" end self.debug_var = str if self.Name == "" and pace.current_part == self and self.pace_properties and IsValid(self.pace_properties["Name"]) then self.pace_properties["Name"]:SetText(self:GetNiceName()) end end else local post_function = self.Functions[self.Function] local input_function = self.Inputs[self.Input] if post_function and input_function then local ran, err = pcall( input_function, self ) if not ran then error("proxy function " .. tostring( self.Input ) .. " | " .. tostring( self.Function ) .. " | " .. tostring( self ) .. " failed: " .. err) end local input_number = err if not isnumber(input_number) then error("proxy function " .. self.Input .. " does not return a number!") end local num = self.Min + (self.Max - self.Min) * ((post_function(((input_number / self.InputDivider) + self.Offset) * self.InputMultiplier, self) + 1) / 2) ^ self.Pow if self.Additive then self.vec_additive[1] = (self.vec_additive[1] or 0) + num num = self.vec_additive[1] end if self.AffectChildren then for _, part in ipairs(self:GetChildren()) do set(self, part, num, nil, nil, true) end else set(self, part, num) end if pace and pace.IsActive() then self.debug_var = math.Round(num, 3) end if self.Name == "" and pace.current_part == self and self.pace_properties and IsValid(self.pace_properties["Name"]) then self.pace_properties["Name"]:SetText(self:GetNiceName()) end end end end BUILDER:Register()