Files
wnsrc/lua/pac3/core/client/parts/proxy.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

1235 lines
31 KiB
Lua

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