mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
56
lua/pac3/extra/client/contraption.lua
Normal file
56
lua/pac3/extra/client/contraption.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local L = pace.LanguageString
|
||||
|
||||
pace.AddTool(L"spawn as props", function(part)
|
||||
local data = pacx.PartToContraptionData(part)
|
||||
net.Start("pac_to_contraption")
|
||||
net.WriteTable(data)
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
function pacx.PartToContraptionData(part, tbl)
|
||||
tbl = tbl or {}
|
||||
|
||||
if part.is_model_part then
|
||||
local data = {}
|
||||
|
||||
local color = part:GetColor()
|
||||
local alpha = part:GetAlpha()
|
||||
|
||||
if part.ProperColorRange then
|
||||
data.clr = Color(color.r * 255, color.g * 255, color.b * 255, alpha * 255)
|
||||
else
|
||||
data.clr = Color(color.r, color.g, color.b, alpha * 255)
|
||||
end
|
||||
|
||||
data.ang = part:GetOwner():GetAngles()
|
||||
data.pos = part:GetOwner():GetPos()
|
||||
data.mat = part:GetMaterial()
|
||||
data.mdl = part:GetModel()
|
||||
data.skn = part:GetSkin()
|
||||
|
||||
local size = part:GetSize()
|
||||
data.scale = part:GetScale()*size
|
||||
|
||||
data.id = part.UniqueID
|
||||
|
||||
table.insert(tbl, data)
|
||||
end
|
||||
|
||||
for key, part in ipairs(part:GetChildren()) do
|
||||
if part.is_model_part then
|
||||
pacx.PartToContraptionData(part, tbl)
|
||||
end
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
17
lua/pac3/extra/client/init.lua
Normal file
17
lua/pac3/extra/client/init.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pacx = pacx or {}
|
||||
|
||||
include("pac3/extra/shared/init.lua")
|
||||
|
||||
include("contraption.lua")
|
||||
include("pac2_compat.lua")
|
||||
include("wire_expression_extension.lua")
|
||||
590
lua/pac3/extra/client/pac2_compat.lua
Normal file
590
lua/pac3/extra/client/pac2_compat.lua
Normal file
@@ -0,0 +1,590 @@
|
||||
--[[
|
||||
| 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 bones = {
|
||||
["pelvis"] = "valvebiped.bip01_pelvis",
|
||||
["spine"] = "valvebiped.bip01_spine",
|
||||
["spine 2"] = "valvebiped.bip01_spine1",
|
||||
["spine 3"] = "valvebiped.bip01_spine2",
|
||||
["spine 4"] = "valvebiped.bip01_spine4",
|
||||
["neck"] = "valvebiped.bip01_neck1",
|
||||
["head"] = "valvebiped.bip01_head1",
|
||||
["right clavicle"] = "valvebiped.bip01_r_clavicle",
|
||||
["right upper arm"] = "valvebiped.bip01_r_upperarm",
|
||||
["right upperarm"] = "valvebiped.bip01_r_upperarm",
|
||||
["right forearm"] = "valvebiped.bip01_r_forearm",
|
||||
["right hand"] = "valvebiped.bip01_r_hand",
|
||||
["left clavicle"] = "valvebiped.bip01_l_clavicle",
|
||||
["left upper arm"] = "valvebiped.bip01_l_upperarm",
|
||||
["left upperarm"] = "valvebiped.bip01_l_upperarm",
|
||||
["left forearm"] = "valvebiped.bip01_l_forearm",
|
||||
["left hand"] = "valvebiped.bip01_l_hand",
|
||||
["right thigh"] = "valvebiped.bip01_r_thigh",
|
||||
["right calf"] = "valvebiped.bip01_r_calf",
|
||||
["right foot"] = "valvebiped.bip01_r_foot",
|
||||
["right toe"] = "valvebiped.bip01_r_toe0",
|
||||
["left thigh"] = "valvebiped.bip01_l_thigh",
|
||||
["left calf"] = "valvebiped.bip01_l_calf",
|
||||
["left foot"] = "valvebiped.bip01_l_foot",
|
||||
["left toe"] = "valvebiped.bip01_l_toe0",
|
||||
}
|
||||
|
||||
local function translate_bone(bone)
|
||||
if bones[bone] then return bones[bone] end
|
||||
if not bone.lower then debug.Trace() return "" end
|
||||
bone = bone:lower()
|
||||
for key, val in pairs(bones) do
|
||||
if bone == val then
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
return bone
|
||||
end
|
||||
|
||||
function pacx.ConvertPAC2Config(data, name)
|
||||
local _out = {}
|
||||
|
||||
local base = pac.CreatePart("group")
|
||||
base:SetName(name or "pac2 outfit")
|
||||
|
||||
for key, data in pairs(data.parts) do
|
||||
if data.sprite.Enabled then
|
||||
local part = pac.CreatePart("sprite")
|
||||
part:SetParent(base)
|
||||
part.pac2_part = data
|
||||
part:SetName(data.name .. " sprite")
|
||||
|
||||
part:SetBone(translate_bone(data.bone))
|
||||
|
||||
part:SetColor(Vector(data.sprite.color.r, data.sprite.color.g, data.sprite.color.b))
|
||||
part:SetAlpha(data.sprite.color.a / 255)
|
||||
|
||||
part:SetPosition(data.offset*1)
|
||||
part:SetAngles(data.angles*1)
|
||||
--part:SetAngleVelocity(Angle(data.anglevelocity.p, -data.anglevelocity.r, data.anglevelocity.y)*0.5)
|
||||
|
||||
part:SetMaterial(data.sprite.material)
|
||||
part:SetSizeX(data.sprite.x)
|
||||
part:SetSizeY(data.sprite.y)
|
||||
part:SetEyeAngles(data.eyeangles)
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part.Name .. " weapon class")
|
||||
part_:SetParent(part)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
|
||||
if data.light.Enabled then
|
||||
local part = pac.CreatePart("light")
|
||||
part:SetParent(base)
|
||||
part.pac2_part = data
|
||||
part:SetName(data.name .. " light")
|
||||
|
||||
part:SetBone(translate_bone(data.bone))
|
||||
|
||||
part:SetColor(Vector(data.light.r, data.light.g, data.light.b))
|
||||
|
||||
part:SetPosition(data.offset*1)
|
||||
part:SetAngles(data.angles*1)
|
||||
--part:SetAngleVelocity(Angle(data.anglevelocity.p, -data.anglevelocity.r, data.anglevelocity.y)*0.5)
|
||||
|
||||
part:SetBrightness(data.light.Brightness)
|
||||
part:SetSize(data.light.Size)
|
||||
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part.Name .. " weapon class")
|
||||
part_:SetParent(part)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
|
||||
if data.text.Enabled then
|
||||
local part = pac.CreatePart("text")
|
||||
part:SetParent(base)
|
||||
part.pac2_part = data
|
||||
part:SetName(data.name .. " text")
|
||||
|
||||
part:SetBone(translate_bone(data.bone))
|
||||
|
||||
part:SetColor(Vector(data.text.color.r, data.text.color.g, data.text.color.b))
|
||||
part:SetAlpha(data.text.color.a / 255)
|
||||
|
||||
part:SetColor(Vector(data.text.outlinecolor.r, data.text.outlinecolor.g, data.text.outlinecolor.b))
|
||||
part:SetAlpha(data.text.outlinecolor.a / 255)
|
||||
|
||||
part:SetPosition(data.offset*1)
|
||||
part:SetAngles(data.angles*1)
|
||||
--part:SetAngleVelocity(Angle(data.anglevelocity.p, -data.anglevelocity.r, data.anglevelocity.y)*0.5)
|
||||
|
||||
part:SetOutline(data.text.outline)
|
||||
part:SetText(data.text.text)
|
||||
part:SetFont(data.text.font)
|
||||
part:SetSize(data.text.size)
|
||||
part:SetEyeAngles(data.eyeangles)
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part.Name .. " weapon class")
|
||||
part_:SetParent(part)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
|
||||
if data.trail.Enabled then
|
||||
local part = pac.CreatePart("trail")
|
||||
part:SetParent(base)
|
||||
part.pac2_part = data
|
||||
part:SetName(data.name .. " trail")
|
||||
part:SetBone(translate_bone(data.bone))
|
||||
|
||||
part:SetPosition(data.offset*1)
|
||||
part:SetAngles(data.angles*1)
|
||||
--part:SetAngleVelocity(Angle(data.anglevelocity.p, -data.anglevelocity.r, data.anglevelocity.y)*0.5)
|
||||
|
||||
part:SetStartSize(data.trail.startsize)
|
||||
|
||||
part:SetStartColor(Vector(data.trail.color.r, data.trail.color.g, data.trail.color.b))
|
||||
part:SetEndColor(Vector(data.trail.color.r, data.trail.color.g, data.trail.color.b))
|
||||
|
||||
part:SetStartAlpha(data.trail.color.a/255)
|
||||
part:SetEndAlpha(data.trail.color.a/255)
|
||||
|
||||
part:SetSpacing(0)
|
||||
|
||||
part:SetMaterial(data.trail.material)
|
||||
part:SetLength(data.trail.length)
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part.Name .. " weapon class")
|
||||
part_:SetParent(part)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
|
||||
if true or data.color.a ~= 0 and data.size ~= 0 and data.scale ~= vector_origin or data.effect.Enabled then
|
||||
local part = pac.CreatePart("model")
|
||||
part:SetParent(base)
|
||||
part.pac2_part = data
|
||||
part:SetName(data.name .. " model")
|
||||
part:SetBone(translate_bone(data.bone))
|
||||
|
||||
part:SetMaterial(data.material)
|
||||
|
||||
part:SetColor(Vector(data.color.r, data.color.g, data.color.b))
|
||||
part:SetAlpha(data.color.a / 255)
|
||||
|
||||
part:SetModel(data.model)
|
||||
part:SetSize(data.size)
|
||||
part:SetScale(data.scale*1)
|
||||
|
||||
part:SetPosition(data.offset*1)
|
||||
part:SetAngles(data.angles*1)
|
||||
--part:SetAngleVelocity(Angle(data.anglevelocity.p, -data.anglevelocity.r, data.anglevelocity.y)*0.5)
|
||||
|
||||
part:SetInvert(data.mirrored)
|
||||
part:SetFullbright(data.fullbright)
|
||||
part:SetEyeAngles(data.eyeangles)
|
||||
|
||||
if data.effect.Enabled then
|
||||
local part2 = pac.CreatePart("effect")
|
||||
part2:SetName(data.name .. " effect")
|
||||
part2:SetParent(part)
|
||||
part2:SetBone(translate_bone(data.bone))
|
||||
|
||||
part2:SetLoop(data.effect.loop)
|
||||
part2:SetRate(data.effect.rate)
|
||||
part2:SetEffect(data.effect.effect)
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part2.Name .. " weapon class")
|
||||
part_:SetParent(part2)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
|
||||
if data.clip.Enabled then
|
||||
local part2 = part:CreatePart("clip")
|
||||
part2:SetName(data.name .. " clip")
|
||||
if data.clip.bone and data.clip.bone ~= "" then
|
||||
part2:SetBone(data.clip.bone)
|
||||
end
|
||||
part2:SetParent(part)
|
||||
part2:SetPosition(data.clip.angles:Forward() * data.clip.distance)
|
||||
part2:SetAngles(data.clip.angles*-1)
|
||||
end
|
||||
|
||||
if data.animation.Enabled then
|
||||
local part2 = part:CreatePart("animation")
|
||||
part2:SetParent(part)
|
||||
part2:SetName(data.name .. " animation")
|
||||
part2:SetSequenceName(data.animation.sequence or "")
|
||||
part2:SetRate(data.animation.rate)
|
||||
part2:SetMin(data.animation.min)
|
||||
part2:SetMax(data.animation.max)
|
||||
part2:SetOffset(data.animation.offset)
|
||||
part2:SetPingPongLoop(data.animation.loopmode)
|
||||
part:AddChild(part2)
|
||||
end
|
||||
|
||||
if data.modelbones.Enabled then
|
||||
part:SetBoneMerge(data.modelbones.merge)
|
||||
part.pac2_modelbone = data.modelbones.redirectparent
|
||||
|
||||
for key, bone in pairs(data.modelbones.bones) do
|
||||
bone.size = tonumber(bone.size)
|
||||
if
|
||||
bone.scale == Vector(1,1,1) and
|
||||
bone.angles == Vector(0,0,0) and
|
||||
bone.offset == Vector(0,0,0) and
|
||||
bone.size == 1
|
||||
then goto CONTINUE end
|
||||
|
||||
local part2 = pac.CreatePart("bone")
|
||||
part2:SetName("model bone " .. part:GetName() .. " " .. key)
|
||||
part2:SetParent(part)
|
||||
part2:SetBone(part:GetOwner():GetBoneName(key))
|
||||
|
||||
part2:SetScale(bone.scale*1)
|
||||
part2:SetAngles(bone.angles*1)
|
||||
part2:SetPosition(bone.offset*1)
|
||||
|
||||
part2:SetSize(bone.size)
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
if data.weaponclass and data.weaponclass ~= "" then
|
||||
local part_ = pac.CreatePart("event")
|
||||
part_:SetName(part.Name .. " weapon class")
|
||||
part_:SetParent(part)
|
||||
part_:SetEvent("weapon_class")
|
||||
part_:SetOperator("find simple")
|
||||
part_:SetInvert(true)
|
||||
part_:SetArguments(data.weaponclass .. "@@" .. (data.hideweaponclass and "1" or "0"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local part = pac.CreatePart("entity")
|
||||
part:SetParent(base)
|
||||
part:SetName("player")
|
||||
|
||||
part:SetColor(Vector(data.player_color.r, data.player_color.g, data.player_color.b))
|
||||
part:SetAlpha(data.player_color.a/255)
|
||||
part:SetMaterial(data.player_material)
|
||||
part:SetScale(data.overall_scale*1)
|
||||
part:SetDrawWeapon(data.drawwep)
|
||||
|
||||
for bone, data in pairs(data.bones) do
|
||||
local part_ = pac.CreatePart("bone")
|
||||
part_:SetParent(part)
|
||||
part_:SetName(bone .. " bone")
|
||||
part_:SetBone(translate_bone(bone))
|
||||
part_:SetSize(tonumber(data.size))
|
||||
part_:SetScale(data.scale*1)
|
||||
part_:SetPosition(data.offset*1)
|
||||
part_:SetAngles(data.angles*1)
|
||||
end
|
||||
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
if part.pac2_part and part.pac2_part.parent and part.pac2_part.parent ~= "none" then
|
||||
for key, parent in pairs(pac.GetLocalParts()) do
|
||||
if parent:GetName() == (part.pac2_part.parent .. " model") then
|
||||
part:SetParent(parent)
|
||||
if parent.pac2_modelbone then
|
||||
part:SetBone(translate_bone(parent.pac2_modelbone))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- hacks
|
||||
|
||||
for key, part in pairs(pac.GetLocalParts()) do
|
||||
part:SetParent(part:GetParent())
|
||||
end
|
||||
|
||||
return base
|
||||
end
|
||||
|
||||
local glon = {}
|
||||
|
||||
do
|
||||
local function Read(reader, rtabs)
|
||||
local t, pos = reader:Peek()
|
||||
if not t then
|
||||
error(string.format("Expected type ID at %s! (Got EOF)",
|
||||
pos))
|
||||
else
|
||||
local dt = decode_types[string.byte(t)]
|
||||
if not dt then
|
||||
error(string.format("Unknown type ID, %s!",
|
||||
string.byte(t)))
|
||||
else
|
||||
return dt(reader, rtabs or {0})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local decode_types
|
||||
decode_types = {
|
||||
-- \2\6omg\1\6omgavalue\1\1
|
||||
[2 ] = function(reader, rtabs) -- table
|
||||
local t, c, pos = {}, reader:Next()
|
||||
rtabs[#rtabs+1] = t
|
||||
local stage = false
|
||||
local key
|
||||
while true do
|
||||
c, pos = reader:Peek()
|
||||
if c == "\1" then
|
||||
if stage then
|
||||
error(string.format("Expected value to match key at %s! (Got EO Table)",
|
||||
pos))
|
||||
else
|
||||
reader:Next()
|
||||
return t
|
||||
end
|
||||
else
|
||||
if stage then
|
||||
t[key] = Read(reader, rtabs)
|
||||
else
|
||||
key = Read(reader, rtabs)
|
||||
end
|
||||
stage = not stage
|
||||
end
|
||||
end
|
||||
end,
|
||||
[3 ] = function(reader, rtabs) -- array
|
||||
local t, i, c, pos = {}, 1, reader:Next()
|
||||
rtabs[#rtabs+1] = t
|
||||
while true do
|
||||
c, pos = reader:Peek()
|
||||
if c == "\1" then
|
||||
reader:Next()
|
||||
return t
|
||||
else
|
||||
t[i] = Read(reader, rtabs)
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
end,
|
||||
[4 ] = function(reader) -- false boolean
|
||||
reader:Next()
|
||||
return false
|
||||
end,
|
||||
[5 ] = function(reader) -- true boolean
|
||||
reader:Next()
|
||||
return true
|
||||
end,
|
||||
[6 ] = function(reader) -- number
|
||||
local s, c, pos, e = "", reader:Next()
|
||||
while true do
|
||||
c = reader:Next()
|
||||
if not c then
|
||||
error(string.format("Expected \1 to end number at %s! (Got EOF!)",
|
||||
pos))
|
||||
elseif c == "\1" then
|
||||
break
|
||||
else
|
||||
s = s..c
|
||||
end
|
||||
end
|
||||
if s == "" then s = "0" end
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
error(string.format("Invalid number at %s! (%q)",
|
||||
pos, s))
|
||||
end
|
||||
return n
|
||||
end,
|
||||
[7 ] = function(reader) -- string
|
||||
local s, c, pos, e = "", reader:Next()
|
||||
while true do
|
||||
c = reader:Next()
|
||||
if not c then
|
||||
error(string.format("Expected unescaped \1 to end string at position %s! (Got EOF)",
|
||||
pos))
|
||||
elseif e then
|
||||
if c == "\3" then
|
||||
s = s.."\0"
|
||||
else
|
||||
s = s..c
|
||||
end
|
||||
e = false
|
||||
elseif c == "\2" then
|
||||
e = true
|
||||
elseif c == "\1" then
|
||||
s = string.gsub(s, "\4", "\"") -- unescape quotes
|
||||
return s
|
||||
else
|
||||
s = s..c
|
||||
end
|
||||
end
|
||||
end,
|
||||
[8 ] = function(reader) -- Vector
|
||||
local x = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local y = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local z = decode_types[6](reader)
|
||||
return Vector(x, y, z)
|
||||
end,
|
||||
[9 ] = function(reader) -- Angle
|
||||
local p = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local y = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local r = decode_types[6](reader)
|
||||
return Angle(p, y, r)
|
||||
end,
|
||||
[13 ] = function(reader) -- ConVar
|
||||
return GetConVar(decode_types[7](reader))
|
||||
end,
|
||||
[15 ] = function(reader) -- Color
|
||||
local r = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local g = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local b = decode_types[6](reader)
|
||||
reader:StepBack()
|
||||
local a = decode_types[6](reader)
|
||||
return Color(r, g, b, a)
|
||||
end,
|
||||
[253] = function(reader) -- -math.huge
|
||||
reader:Next()
|
||||
return -math.huge
|
||||
end,
|
||||
[254] = function(reader) -- math.huge
|
||||
reader:Next()
|
||||
return math.huge
|
||||
end,
|
||||
[255] = function(reader, rtabs) -- Reference
|
||||
return rtabs[decode_types[6](reader) - 1]
|
||||
end,
|
||||
}
|
||||
local reader_meta = {}
|
||||
reader_meta.__index = reader_meta
|
||||
function reader_meta:Next()
|
||||
self.i = self.i+1
|
||||
self.c = string.sub(self.s, self.i, self.i)
|
||||
if self.c == "" then self.c = nil end
|
||||
self.p = string.sub(self.s, self.i+1, self.i+1)
|
||||
if self.p == "" then self.p = nil end
|
||||
return self.c, self.i
|
||||
end
|
||||
function reader_meta:StepBack()
|
||||
self.i = self.i-1
|
||||
self.c = string.sub(self.s, self.i, self.i)
|
||||
if self.c == "" then self.c = nil end
|
||||
self.p = string.sub(self.s, self.i+1, self.i+1)
|
||||
if self.p == "" then self.p = nil end
|
||||
return self.c, self.i
|
||||
end
|
||||
function reader_meta:Peek()
|
||||
return self.p, self.i+1
|
||||
end
|
||||
function glon.decode(data)
|
||||
if data == nil then
|
||||
return nil
|
||||
elseif not isstring(data) then
|
||||
error(string.format("Expected string to decode! (Got type %s)",
|
||||
type(data)
|
||||
))
|
||||
elseif data:len() == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
return Read(setmetatable({
|
||||
s = data,
|
||||
i = 0,
|
||||
c = string.sub(data, 0, 0),
|
||||
p = string.sub(data, 1, 1),
|
||||
}, reader_meta), {})
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("pac_convert_pac2_outfits", function()
|
||||
if not file.IsDir("pac2_outfits", "DATA") then
|
||||
pac.Message("garrysmod/data/pac2_outfits/ does not exist")
|
||||
return
|
||||
end
|
||||
|
||||
local folders = select(2, file.Find("pac2_outfits/*", "DATA"))
|
||||
|
||||
if #folders == 0 then
|
||||
pac.Message("garrysmod/data/pac2_outfits/ is empty")
|
||||
return
|
||||
end
|
||||
|
||||
for _, uniqueid in ipairs(folders) do
|
||||
local owner_nick = file.Read("pac2_outfits/" .. uniqueid .. "/__owner.txt", "DATA")
|
||||
|
||||
if not owner_nick then
|
||||
owner_nick = pac.LocalPlayer:Nick()
|
||||
pac.Message("garrysmod/data/pac2_outfits/" .. uniqueid .. "/__owner.txt does not exist (it contains the player nickname) defaulting to " .. owner_nick)
|
||||
end
|
||||
|
||||
local folders = select(2, file.Find("pac2_outfits/" .. uniqueid .. "/*", "DATA"))
|
||||
|
||||
if #folders == 0 then
|
||||
pac.Message("garrysmod/data/pac2_outfits/" .. uniqueid .. "/ is empty")
|
||||
return
|
||||
end
|
||||
|
||||
for _, folder_name in ipairs(folders) do
|
||||
local name = file.Read("pac2_outfits/" .. uniqueid .. "/" .. folder_name .. "/name.txt", "DATA")
|
||||
local data = file.Read("pac2_outfits/" .. uniqueid .. "/" .. folder_name .. "/outfit.txt", "DATA")
|
||||
|
||||
if not name then
|
||||
pac.Message("garrysmod/data/pac2_outfits/" .. uniqueid .. "/" .. folder_name .. "/name.txt does not exist. defaulting to: " .. folder_name)
|
||||
end
|
||||
|
||||
if data then
|
||||
pace.ClearParts()
|
||||
|
||||
local ok, res = pcall(function() pacx.ConvertPAC2Config(glon.decode(data), name) end)
|
||||
if ok then
|
||||
file.CreateDir("pac3/pac2_outfits/")
|
||||
file.CreateDir("pac3/pac2_outfits/" .. uniqueid .. "/")
|
||||
|
||||
pace.SaveParts("pac2_outfits/" .. uniqueid .. "/" .. folder_name)
|
||||
else
|
||||
pac.Message("garrysmod/data/pac2_outfits/" .. uniqueid .. "/" .. folder_name .. "(" .. name .. ") failed to convert : " .. res)
|
||||
end
|
||||
else
|
||||
pac.Message("garrysmod/data/pac2_outfits/" .. uniqueid .. "/" .. folder_name .. "/data.txt does not exist. this file contains the outfit data")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pace.ClearParts()
|
||||
|
||||
pac.Message("pac2 outfits are stored under pac > load > pac2_outfits in the editor")
|
||||
pac.Message("you may need to restart the editor to see them")
|
||||
end)
|
||||
96
lua/pac3/extra/client/wire_expression_extension.lua
Normal file
96
lua/pac3/extra/client/wire_expression_extension.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
--[[
|
||||
| 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 IsValid = IsValid
|
||||
|
||||
hook.Add("pac_Initialized", "pac_e2_extension", function()
|
||||
if E2Helper then
|
||||
E2Helper.Descriptions["pacSetKeyValue"] = "Sets a property value on given part. Part unique id is recommended but you can also input name."
|
||||
end
|
||||
end)
|
||||
|
||||
local function SetKeyValue(ply, ent, unique_id, key, val)
|
||||
local set = "Set" .. key
|
||||
|
||||
if ent == game.GetWorld() then ent = pac.WorldEntity end
|
||||
|
||||
local part = pac.GetPartFromUniqueID(pac.Hash(ply), unique_id)
|
||||
if not IsValid( part ) then return end
|
||||
|
||||
if part:GetRootPart():GetOwner() == ent then
|
||||
if key == "EventHide" then
|
||||
part:SetEventTrigger(ent, val > 0)
|
||||
else
|
||||
local t1 = type(part[key])
|
||||
local t2 = type(val)
|
||||
|
||||
if t1 == "boolean" and t2 == "number" then
|
||||
t2 = "boolean"
|
||||
val = val > 0
|
||||
end
|
||||
|
||||
if t1 == t2 then
|
||||
part[set](part, val)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("pac_e2_setkeyvalue_str", function()
|
||||
local ply = net.ReadEntity()
|
||||
|
||||
if ply:IsValid() then
|
||||
local ent = net.ReadEntity()
|
||||
local id = net.ReadString()
|
||||
local key = net.ReadString()
|
||||
local val = net.ReadString()
|
||||
|
||||
SetKeyValue(ply, ent, id, key, val)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("pac_e2_setkeyvalue_vec", function()
|
||||
local ply = net.ReadEntity()
|
||||
|
||||
if ply:IsValid() then
|
||||
local ent = net.ReadEntity()
|
||||
local id = net.ReadString()
|
||||
local key = net.ReadString()
|
||||
local val = net.ReadVector()
|
||||
|
||||
SetKeyValue(ply, ent, id, key, val)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("pac_e2_setkeyvalue_ang", function()
|
||||
local ply = net.ReadEntity()
|
||||
|
||||
if ply:IsValid() then
|
||||
local ent = net.ReadEntity()
|
||||
local id = net.ReadString()
|
||||
local key = net.ReadString()
|
||||
local val = net.ReadAngle()
|
||||
|
||||
SetKeyValue(ply, ent, id, key, val)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("pac_e2_setkeyvalue_num", function()
|
||||
local ply = net.ReadEntity()
|
||||
|
||||
if ply:IsValid() then
|
||||
local ent = net.ReadEntity()
|
||||
local id = net.ReadString()
|
||||
local key = net.ReadString()
|
||||
local val = net.ReadFloat()
|
||||
|
||||
SetKeyValue(ply, ent, id, key, val)
|
||||
end
|
||||
end)
|
||||
117
lua/pac3/extra/server/contraption.lua
Normal file
117
lua/pac3/extra/server/contraption.lua
Normal file
@@ -0,0 +1,117 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
util.AddNetworkString("pac_to_contraption")
|
||||
|
||||
local receieved = {}
|
||||
|
||||
local function spawn(val,ply)
|
||||
local model = val.mdl
|
||||
|
||||
if not model or model == "" or model:find("\n") or model:find("..", 1, true) then
|
||||
return
|
||||
end
|
||||
|
||||
pace.suppress_prop_spawn = true
|
||||
local ok = hook.Run("PlayerSpawnProp", ply, model)
|
||||
pace.suppress_prop_spawn = false
|
||||
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
local ent = ents.Create("prop_physics")
|
||||
|
||||
SafeRemoveEntity(receieved[val.id])
|
||||
receieved[val.id] = ent
|
||||
|
||||
ent:SetModel(model)-- insecure
|
||||
ent:SetPos(val.pos)-- insecure
|
||||
ent:SetAngles(val.ang)-- insecure
|
||||
ent:SetSkin(val.skn)
|
||||
ent:SetMaterial(val.mat) -- insecure
|
||||
ent:Spawn()
|
||||
ent:SetHealth(9999999)
|
||||
ent:SetColor(val.clr)
|
||||
ent:SetRenderMode( RENDERMODE_TRANSCOLOR )
|
||||
|
||||
hook.Run("PlayerSpawnedProp", ply, model, ent)
|
||||
|
||||
if ent.CPPISetOwner and not (ent:CPPIGetOwner() or NULL):IsValid() then
|
||||
ent:CPPISetOwner(ply)
|
||||
end
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
|
||||
if phys:IsValid() then
|
||||
phys:EnableMotion(false)
|
||||
|
||||
local maxabs = 150
|
||||
|
||||
val.scale.X = math.Clamp(val.scale.X,-maxabs,maxabs)
|
||||
val.scale.Y = math.Clamp(val.scale.Y,-maxabs,maxabs)
|
||||
val.scale.Z = math.Clamp(val.scale.Z,-maxabs,maxabs)
|
||||
|
||||
for i=0, ent:GetBoneCount()-1 do
|
||||
ent:ManipulateBoneScale( i, val.scale )
|
||||
end
|
||||
|
||||
undo.Create("Prop")
|
||||
undo.SetPlayer(ply)
|
||||
undo.AddEntity(ent)
|
||||
undo.Finish("Prop ("..tostring(model)..")")
|
||||
|
||||
ply:AddCleanup("props", ent)
|
||||
|
||||
gamemode.Call("PlayerSpawnedProp", ply, model, ent)
|
||||
else
|
||||
ent:Remove()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local pac_to_contraption_allow = CreateConVar("pac_to_contraption_allow", "1")
|
||||
local max_contraptions = CreateConVar("pac_max_contraption_entities", 60)
|
||||
|
||||
pace.PCallNetReceive(net.Receive, "pac_to_contraption", function(len, ply)
|
||||
if not pac_to_contraption_allow:GetBool() then
|
||||
net.Start("pac_submit_acknowledged")
|
||||
net.WriteBool(false)
|
||||
net.WriteString("This server does not allow spawning PAC contraptions.")
|
||||
net.Send(ply)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if len < 64 then return end
|
||||
|
||||
local allowed = pac.RatelimitPlayer( ply, "pac_to_contraption", 5, 1, {"Player ", ply, " is spamming pac_to_contraption!"} )
|
||||
if not allowed then return end
|
||||
|
||||
local data = net.ReadTable()
|
||||
|
||||
local max = max_contraptions:GetInt()
|
||||
local count = table.Count(data)
|
||||
if count > max then
|
||||
net.Start("pac_submit_acknowledged")
|
||||
net.WriteBool(false)
|
||||
net.WriteString("You can only spawn " .. max .. " props at a time!")
|
||||
net.Send(ply)
|
||||
|
||||
pac.Message(ply, " might have tried to crash the server by attempting to spawn ", count, " entities with the contraption system!")
|
||||
return
|
||||
end
|
||||
|
||||
pac.Message("Spawning contraption by ", ply, " with ", count, " entities")
|
||||
|
||||
for key, val in pairs(data) do
|
||||
spawn(val,ply)
|
||||
end
|
||||
end)
|
||||
16
lua/pac3/extra/server/init.lua
Normal file
16
lua/pac3/extra/server/init.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pacx = pacx or {}
|
||||
|
||||
include("pac3/extra/shared/init.lua")
|
||||
|
||||
include("contraption.lua")
|
||||
include("map_outfit.lua")
|
||||
163
lua/pac3/extra/server/map_outfit.lua
Normal file
163
lua/pac3/extra/server/map_outfit.lua
Normal file
@@ -0,0 +1,163 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
pacx.SpawnedMapEntities = pacx.SpawnedMapEntities or {}
|
||||
|
||||
local VEC0 = Vector(0, 0, 0)
|
||||
local ANG0 = Angle(0, 0, 0)
|
||||
local VEC1 = Vector(1, 1, 1)
|
||||
|
||||
local function tocolor(v, a) return Color(v.x, v.y, v.z, a) end
|
||||
|
||||
local function has_clip(part)
|
||||
for key, part in pairs(part.children) do
|
||||
if part.self.ClassName == "clip" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function set_pos(ent, data, parent)
|
||||
local ppos = Vector()
|
||||
local pang = Angle()
|
||||
|
||||
if parent:IsValid() then
|
||||
ppos = parent:GetPos()
|
||||
pang = parent:GetAngles()
|
||||
end
|
||||
|
||||
local pos, ang = LocalToWorld((data.Position or VEC0)+ (data.PositionOffset or VEC0), (data.Angles or ANG0) + (data.AngleOffset or ANG0), ppos, pang)
|
||||
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
end
|
||||
|
||||
local spawn_handlers = {
|
||||
model = function(part, parent)
|
||||
if
|
||||
(not part.self.Alpha or part.self.Alpha ~= 0) and
|
||||
(not part.self.Size or part.self.Size ~= 0) and
|
||||
(not part.self.Scale or part.self.Scale ~= VEC0)
|
||||
then
|
||||
SafeRemoveEntity(pacx.SpawnedMapEntities[part.self.UniqueID])
|
||||
|
||||
local ent = ents.Create("prop_dynamic")
|
||||
|
||||
local data = part.self
|
||||
|
||||
ent:SetModel(data.Model or "models/dav0r/hoverball.mdl")
|
||||
|
||||
local c = data.Color
|
||||
|
||||
if c and data.Brightness then
|
||||
c = c * data.Brightness
|
||||
end
|
||||
|
||||
if data.Color then ent:SetColor(tocolor(c, (data.Alpha or 1) * 255)) end
|
||||
if data.Skin then ent:SetSkin(data.Skin) end
|
||||
if data.Material then ent:SetMaterial(data.Material) end
|
||||
if data.Size then ent:SetModelScale(data.Size, 0) end
|
||||
ent:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
set_pos(ent, part.self, parent)
|
||||
|
||||
ent:Spawn()
|
||||
|
||||
pacx.SpawnedMapEntities[part.self.UniqueID] = ent
|
||||
|
||||
return ent
|
||||
end
|
||||
end,
|
||||
|
||||
light = function(part, parent)
|
||||
|
||||
SafeRemoveEntity(pacx.SpawnedMapEntities[part.self.UniqueID])
|
||||
|
||||
local ent = ents.Create("light_dynamic")
|
||||
|
||||
local data = part.self
|
||||
|
||||
local c = data.Color
|
||||
|
||||
ent:SetKeyValue("_light", ("%i %i %i 255"):format(c and c.x or 255, c and c.y or 255, c and c.z or 255))
|
||||
ent:SetKeyValue("brightness", (data.Brightness or 1) * 8)
|
||||
if data.Size then ent:SetKeyValue("distance", data.Size) end
|
||||
if data.Style then ent:SetKeyValue("style", data.Style) end
|
||||
|
||||
set_pos(ent, part.self, parent)
|
||||
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
pacx.SpawnedMapEntities[part.self.UniqueID] = ent
|
||||
|
||||
end,
|
||||
|
||||
effect = function(part, parent)
|
||||
if parent:IsValid() and part.data.Effect then
|
||||
ParticleEffectAttach(part.data.Effect, PATTACH_ABSORIGIN_FOLLOW, parent, 0)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local function try_spawn(part, parent)
|
||||
|
||||
local func = spawn_handlers[part.self.ClassName]
|
||||
|
||||
if func then
|
||||
return func(part, parent)
|
||||
else
|
||||
pac.Message(part.self.ClassName)
|
||||
end
|
||||
|
||||
return NULL
|
||||
end
|
||||
|
||||
local function parse_part_data(part, parent)
|
||||
parent = try_spawn(part, parent)
|
||||
|
||||
for key, part in pairs(part.children) do
|
||||
parse_part_data(part, parent)
|
||||
end
|
||||
end
|
||||
|
||||
function pacx.SpawnMapOutfit(data)
|
||||
if data.self then
|
||||
data = {data}
|
||||
end
|
||||
|
||||
for key, part in pairs(data) do
|
||||
parse_part_data(part, NULL)
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("pac_spawn_map", function(ply, _, args)
|
||||
if not ply:IsAdmin() then return end
|
||||
|
||||
if not args[1] then
|
||||
return ply:PrintMessage(HUD_PRINTCONSOLE, "Please enter a pac name")
|
||||
end
|
||||
|
||||
for _,v in pairs(pacx.SpawnedMapEntities) do
|
||||
SafeRemoveEntity(v)
|
||||
end
|
||||
|
||||
pacx.SpawnedMapEntities = {}
|
||||
|
||||
local data = pace.luadata.ReadFile("pac3/" .. args[1] .. ".txt")
|
||||
|
||||
if data then
|
||||
pacx.SpawnMapOutfit(data)
|
||||
else
|
||||
pac.Message(data)
|
||||
end
|
||||
end)
|
||||
88
lua/pac3/extra/shared/hands.lua
Normal file
88
lua/pac3/extra/shared/hands.lua
Normal 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 SWEP = { Primary = {}, Secondary = {} }
|
||||
|
||||
local baseClass = baseclass.Get( "weapon_base" )
|
||||
|
||||
SWEP.Author = ""
|
||||
SWEP.Contact = ""
|
||||
SWEP.Purpose = ""
|
||||
SWEP.Instructions = "Right-Click to toggle crosshair"
|
||||
SWEP.PrintName = "Hands"
|
||||
SWEP.DrawAmmo = false
|
||||
SWEP.DrawCrosshair = true
|
||||
SWEP.DrawWeaponInfoBox = true
|
||||
|
||||
SWEP.SlotPos = 1
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AdminSpawnable = false
|
||||
|
||||
SWEP.AutoSwitchTo = true
|
||||
SWEP.AutoSwitchFrom = true
|
||||
SWEP.Weight = 1
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
SWEP.ViewModel = "models/weapons/c_arms.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
|
||||
|
||||
function SWEP:DrawHUD() end
|
||||
function SWEP:DrawWorldModel() end
|
||||
function SWEP:DrawWorldModelTranslucent() end
|
||||
function SWEP:CanPrimaryAttack() return false end
|
||||
function SWEP:CanSecondaryAttack() return false end
|
||||
function SWEP:Reload() return false end
|
||||
function SWEP:Holster() return true end
|
||||
function SWEP:ShouldDropOnDie() return false end
|
||||
|
||||
local weaponSelectionColor = Color( 255, 220, 0, 255 )
|
||||
function SWEP:DrawWeaponSelection( x, y, w, t, a )
|
||||
weaponSelectionColor.a = a
|
||||
draw.SimpleText( "C", "creditslogo", x + w / 2, y, weaponSelectionColor, TEXT_ALIGN_CENTER )
|
||||
|
||||
baseClass.PrintWeaponInfo( self, x + w + 20, y + t * 0.95, alpha )
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
if self.SetHoldType then
|
||||
self:SetHoldType( "normal" )
|
||||
else
|
||||
self:SetWeaponHoldType( "normal" )
|
||||
end
|
||||
|
||||
self:DrawShadow( false )
|
||||
self:SetSequence( "ragdoll" ) -- paired with SWEP.ViewModel = "models/weapons/c_arms.mdl" to make the arms invisible
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
if SERVER then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if not IsFirstTimePredicted() then return end
|
||||
self.DrawCrosshair = not self.DrawCrosshair
|
||||
self:SetNextSecondaryFire( CurTime() + 0.3 )
|
||||
end
|
||||
|
||||
weapons.Register( SWEP, "none", true )
|
||||
37
lua/pac3/extra/shared/init.lua
Normal file
37
lua/pac3/extra/shared/init.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
include("hands.lua")
|
||||
include("pac_weapon.lua")
|
||||
include("projectiles.lua")
|
||||
|
||||
local cvar = CreateConVar("pac_restrictions", "0", FCVAR_REPLICATED)
|
||||
|
||||
if CLIENT then
|
||||
pac.AddHook("pac_EditorCalcView", "pac_restrictions", function()
|
||||
if cvar:GetInt() > 0 and not pac.LocalPlayer:IsAdmin() then
|
||||
local ent = pace.GetViewEntity()
|
||||
local dir = pace.ViewPos - ent:EyePos()
|
||||
local dist = ent:BoundingRadius() * ent:GetModelScale() * 4
|
||||
local filter = player.GetAll()
|
||||
table.insert(filter, ent)
|
||||
|
||||
if dir:Length() > dist then
|
||||
pace.ViewPos = ent:EyePos() + (dir:GetNormalized() * dist)
|
||||
end
|
||||
|
||||
local res = util.TraceHull({start = ent:EyePos(), endpos = pace.ViewPos, filter = filter, mins = Vector(1,1,1)*-8, maxs = Vector(1,1,1)*8})
|
||||
if res.Hit then
|
||||
return res.HitPos
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
99
lua/pac3/extra/shared/pac_weapon.lua
Normal file
99
lua/pac3/extra/shared/pac_weapon.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
do return end
|
||||
local SWEP = {Primary = {}, Secondary = {}}
|
||||
|
||||
SWEP.Author = "CapsAdmin"
|
||||
SWEP.Contact = ""
|
||||
SWEP.Purpose = ""
|
||||
SWEP.Instructions = ""
|
||||
SWEP.PrintName = "pac weapon"
|
||||
SWEP.DrawAmmo = false
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.ViewModel = "models/weapons/v_pistol.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_pistol.mdl"
|
||||
SWEP.DrawWeaponInfoBox = true
|
||||
SWEP.Base = "weapon_base"
|
||||
|
||||
SWEP.SlotPos = 1
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AdminSpawnable = true
|
||||
|
||||
SWEP.AutoSwitchTo = true
|
||||
SWEP.AutoSwitchFrom = true
|
||||
SWEP.Weight = 1
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
SWEP.Primary.ClipSize = 10
|
||||
SWEP.Primary.DefaultClip = 10
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "pistol"
|
||||
|
||||
SWEP.Secondary.ClipSize = 10
|
||||
SWEP.Secondary.DefaultClip = 10
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "pistol"
|
||||
|
||||
function SWEP:OnDrop()
|
||||
end
|
||||
|
||||
function SWEP:GetViewModelPosition(pos, ang)
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
function SWEP:TranslateActivity(act)
|
||||
return act
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Initialize() end
|
||||
function SWEP:DrawHUD() end
|
||||
function SWEP:PrintWeaponInfo() end
|
||||
function SWEP:DrawWeaponSelection(x,y,w,t,a) end
|
||||
function SWEP:DrawWorldModel() return true end
|
||||
function SWEP:CanPrimaryAttack() return true end
|
||||
function SWEP:CanSecondaryAttack() return true end
|
||||
function SWEP:Reload() return false end
|
||||
function SWEP:Holster() return true end
|
||||
function SWEP:ShouldDropOnDie() return false end
|
||||
|
||||
weapons.Register(SWEP, "pac_weapon", true)
|
||||
|
||||
if CLIENT then
|
||||
local BUILDER, PART = pac.PartTemplate("base")
|
||||
|
||||
PART.ClassName = "weapon"
|
||||
|
||||
|
||||
BUILDER:StartStorableVars()
|
||||
for key, val in pairs(SWEP) do
|
||||
if not istable(val) and not isfunction(val) then
|
||||
BUILDER:GetSet(key, val)
|
||||
end
|
||||
end
|
||||
|
||||
for key, val in pairs(SWEP.Primary) do
|
||||
BUILDER:GetSet("Primary"..key, val)
|
||||
end
|
||||
|
||||
for key, val in pairs(SWEP.Secondary) do
|
||||
BUILDER:GetSet("Secondary"..key, val)
|
||||
end
|
||||
BUILDER:EndStorableVars()
|
||||
|
||||
BUILDER:Register()
|
||||
end
|
||||
513
lua/pac3/extra/shared/projectiles.lua
Normal file
513
lua/pac3/extra/shared/projectiles.lua
Normal file
@@ -0,0 +1,513 @@
|
||||
--[[
|
||||
| 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 enable = CreateConVar("pac_sv_projectiles", 0, CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||
local pac_sv_projectile_max_attract_radius = CreateConVar("pac_sv_projectile_max_attract_radius", 300, CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||
local pac_sv_projectile_max_damage_radius = CreateConVar("pac_sv_projectile_max_damage_radius", 100, CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||
|
||||
do -- projectile entity
|
||||
local ENT = {}
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_anim"
|
||||
ENT.ClassName = "pac_projectile"
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("Bool", 0, "AimDir")
|
||||
self:NetworkVar("Vector", 0, "OldVelocity")
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function ENT:Draw()
|
||||
if self:GetAimDir() then
|
||||
if self:GetOldVelocity() ~= vector_origin then
|
||||
self:SetRenderAngles(self:GetOldVelocity():Angle())
|
||||
elseif self:GetVelocity() ~= vector_origin then
|
||||
self:SetRenderAngles(self:GetVelocity():Angle())
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetParent():IsValid() and not self.done then
|
||||
self:SetPredictable(true)
|
||||
self.done = true
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("pac_projectile_collide_event", function()
|
||||
local self = net.ReadEntity()
|
||||
local data = net.ReadTable()
|
||||
|
||||
self.pac_event_collision_data = data
|
||||
end)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
pac.AddHook("EntityTakeDamage", "pac_projectile", function(ent, dmg)
|
||||
local a, i = dmg:GetAttacker(), dmg:GetInflictor()
|
||||
|
||||
if a == i and a:IsValid() and a.projectile_owner then
|
||||
local owner = a.projectile_owner
|
||||
if owner:IsValid() then
|
||||
dmg:SetAttacker(a.projectile_owner)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ENT.projectile_owner = NULL
|
||||
|
||||
function ENT:Initialize()
|
||||
self.next_target = 0
|
||||
end
|
||||
|
||||
function ENT:SetData(ply, pos, ang, part)
|
||||
|
||||
self.projectile_owner = ply
|
||||
|
||||
local radius = math.Clamp(part.Radius, 1, pac_sv_projectile_max_damage_radius:GetFloat())
|
||||
|
||||
if part.Sphere then
|
||||
self:PhysicsInitSphere(radius)
|
||||
else
|
||||
self:PhysicsInitBox(Vector(1,1,1) * - radius, Vector(1,1,1) * radius)
|
||||
end
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
phys:EnableGravity(part.Gravity)
|
||||
phys:AddVelocity((ang:Forward() + (VectorRand():Angle():Forward() * part.Spread)) * part.Speed * 1000)
|
||||
if part.AddOwnerSpeed then
|
||||
phys:AddVelocity(ply:GetVelocity())
|
||||
end
|
||||
|
||||
if part.Collisions then
|
||||
if part.CollideWithSelf then
|
||||
self:SetCollisionGroup(COLLISION_GROUP_NONE)
|
||||
else
|
||||
self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE_DEBRIS)
|
||||
end
|
||||
else
|
||||
phys:EnableCollisions(false)
|
||||
end
|
||||
|
||||
phys:SetMass(math.Clamp(part.Mass, 0.001, 50000))
|
||||
phys:SetDamping(0, 0)
|
||||
|
||||
self:SetAimDir(part.AimDir)
|
||||
|
||||
self.part_data = part
|
||||
end
|
||||
|
||||
local damage_types = {
|
||||
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.BlastDamageInfo
|
||||
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,
|
||||
}
|
||||
|
||||
local dissolver_entity = NULL
|
||||
local function dissolve(target, attacker, typ)
|
||||
local ent = dissolver_entity:IsValid() and dissolver_entity or ents.Create("env_entity_dissolver")
|
||||
ent:Spawn()
|
||||
target:SetName(tostring({}))
|
||||
ent:SetKeyValue("dissolvetype", tostring(typ))
|
||||
ent:Fire("Dissolve", target:GetName())
|
||||
timer.Simple(5, function() SafeRemoveEntity(ent) end)
|
||||
dissolver_entity = ent
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if not self.projectile_owner:IsValid() then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PhysicsUpdate(phys)
|
||||
if not self.part_data then return end
|
||||
|
||||
phys:SetVelocity(phys:GetVelocity() / math.max(1 + (self.part_data.Damping / 100), 1))
|
||||
|
||||
if self.part_data.Attract ~= 0 then
|
||||
local ply = self.projectile_owner
|
||||
|
||||
if self.part_data.AttractMode == "hitpos" then
|
||||
local pos = ply:GetEyeTrace().HitPos
|
||||
|
||||
local dir = pos - phys:GetPos()
|
||||
dir:Normalize()
|
||||
dir = dir * self.part_data.Attract
|
||||
|
||||
phys:SetVelocity(phys:GetVelocity() + dir)
|
||||
elseif self.part_data.AttractMode == "hitpos_radius" then
|
||||
local pos = ply:EyePos() + ply:GetAimVector() * self.part_data.AttractRadius
|
||||
|
||||
local dir = pos - phys:GetPos()
|
||||
dir:Normalize()
|
||||
dir = dir * self.part_data.Attract
|
||||
|
||||
phys:SetVelocity(phys:GetVelocity() + dir)
|
||||
elseif self.part_data.AttractMode == "closest_to_projectile" or self.part_data.AttractMode == "closest_to_hitpos" then
|
||||
if self.next_target < CurTime() then
|
||||
local radius = math.Clamp(self.part_data.AttractRadius, 0, pac_sv_projectile_max_attract_radius:GetFloat())
|
||||
local pos
|
||||
|
||||
if self.part_data.AttractMode == "closest_to_projectile" then
|
||||
pos = phys:GetPos()
|
||||
else
|
||||
pos = ply:GetEyeTrace().HitPos
|
||||
end
|
||||
|
||||
local closest_1 = {}
|
||||
local closest_2 = {}
|
||||
|
||||
for _, ent in ipairs(ents.FindInSphere(pos, radius)) do
|
||||
if
|
||||
ent ~= self and
|
||||
ent ~= ply and
|
||||
ent:GetPhysicsObject():IsValid() and
|
||||
ent:GetClass() ~= self:GetClass()
|
||||
then
|
||||
local data = {dist = ent:NearestPoint(ent:GetPos()):Distance(pos), ent = ent}
|
||||
if ent:IsPlayer() or ent:IsNPC() then
|
||||
table.insert(closest_1, data)
|
||||
else
|
||||
table.insert(closest_2, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if closest_1[1] then
|
||||
table.sort(closest_1, function(a, b) return a.dist < b.dist end)
|
||||
self.target_ent = closest_1[1].ent
|
||||
elseif closest_2[1] then
|
||||
table.sort(closest_2, function(a, b) return a.dist < b.dist end)
|
||||
self.target_ent = closest_2[1].ent
|
||||
end
|
||||
|
||||
self.next_target = CurTime() + 0.15
|
||||
end
|
||||
|
||||
if self.target_ent and self.target_ent:IsValid() then
|
||||
local dir = self.target_ent:NearestPoint(phys:GetPos()) - phys:GetPos()
|
||||
dir:Normalize()
|
||||
dir = dir * self.part_data.Attract
|
||||
|
||||
phys:SetVelocity(phys:GetVelocity() + dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
util.AddNetworkString("pac_projectile_collide_event")
|
||||
|
||||
function ENT:PhysicsCollide(data, phys)
|
||||
if not self.part_data then return end
|
||||
if not self.projectile_owner:IsValid() then return end
|
||||
|
||||
net.Start("pac_projectile_collide_event", true)
|
||||
net.WriteEntity(self)
|
||||
net.WriteTable({}) -- nothing for now
|
||||
net.SendPVS(data.HitPos)
|
||||
|
||||
local ply = self.projectile_owner
|
||||
|
||||
if self.part_data.Bounce ~= 0 then
|
||||
phys:SetVelocity(data.OurOldVelocity - 2 * (data.HitNormal:Dot(data.OurOldVelocity) * data.HitNormal) * self.part_data.Bounce)
|
||||
end
|
||||
|
||||
if self.part_data.Sticky and (self.part_data.Bounce == 0 or not data.HitEntity:IsWorld()) then
|
||||
phys:SetVelocity(Vector(0,0,0))
|
||||
phys:Sleep()
|
||||
phys:EnableMotion(false)
|
||||
phys:EnableCollisions(false)
|
||||
|
||||
timer.Simple(0, function() if self:IsValid() then self:SetCollisionGroup(COLLISION_GROUP_DEBRIS) end end)
|
||||
|
||||
if not data.HitEntity:IsWorld() then
|
||||
if data.HitEntity:GetBoneCount() then
|
||||
local closest = {}
|
||||
for id = 1, data.HitEntity:GetBoneCount() do
|
||||
local pos = data.HitEntity:GetBonePosition(id)
|
||||
if pos then
|
||||
table.insert(closest, {dist = pos:Distance(data.HitPos), id = id, pos = pos})
|
||||
end
|
||||
end
|
||||
if closest[1] then
|
||||
table.sort(closest, function(a, b) return a.dist < b.dist end)
|
||||
self:FollowBone(data.HitEntity, closest[1].id)
|
||||
self:SetLocalPos(util.TraceLine({start = data.HitPos, endpos = closest[1].pos}).HitPos - closest[1].pos)
|
||||
else
|
||||
self:SetPos(data.HitPos)
|
||||
self:SetParent(data.HitEntity)
|
||||
end
|
||||
else
|
||||
self:SetPos(data.HitPos)
|
||||
self:SetParent(data.HitEntity)
|
||||
end
|
||||
end
|
||||
|
||||
self:SetOldVelocity(data.OurOldVelocity)
|
||||
end
|
||||
|
||||
if self.part_data.BulletImpact then
|
||||
self:FireBullets{
|
||||
Attacker = ply,
|
||||
Damage = 0,
|
||||
Force = 0,
|
||||
Num = 1,
|
||||
Src = data.HitPos - data.HitNormal,
|
||||
Dir = data.HitNormal,
|
||||
Distance = 10,
|
||||
}
|
||||
end
|
||||
|
||||
if self.part_data.DamageType:sub(0, 9) == "dissolve_" and damage_types[self.part_data.DamageType] then
|
||||
if data.HitEntity:IsPlayer() then
|
||||
local info = DamageInfo()
|
||||
info:SetAttacker(ply)
|
||||
info:SetInflictor(self)
|
||||
info:SetDamageForce(data.OurOldVelocity)
|
||||
info:SetDamagePosition(data.HitPos)
|
||||
info:SetDamage(100000)
|
||||
info:SetDamageType(damage_types.dissolve)
|
||||
|
||||
data.HitEntity:TakeDamageInfo(info)
|
||||
else
|
||||
local can = hook.Run("CanProperty", ply, "remover", data.HitEntity)
|
||||
if can ~= false then
|
||||
dissolve(data.HitEntity, ply, damage_types[self.part_data.DamageType])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local damage_radius = math.Clamp(self.part_data.DamageRadius, 0, 300)
|
||||
|
||||
if self.part_data.Damage > 0 then
|
||||
if self.part_data.DamageType == "heal" then
|
||||
if damage_radius > 0 then
|
||||
for _, ent in ipairs(ents.FindInSphere(data.HitPos, damage_radius)) do
|
||||
if ent ~= ply or self.part_data.CollideWithOwner then
|
||||
ent:SetHealth(math.min(ent:Health() + self.part_data.Damage, ent:GetMaxHealth()))
|
||||
end
|
||||
end
|
||||
else
|
||||
data.HitEntity:SetHealth(math.min(data.HitEntity:Health() + self.part_data.Damage, data.HitEntity:GetMaxHealth()))
|
||||
end
|
||||
elseif self.part_data.DamageType == "armor" then
|
||||
if damage_radius > 0 then
|
||||
for _, ent in ipairs(ents.FindInSphere(data.HitPos, damage_radius)) do
|
||||
if ent.SetArmor and ent.Armor then
|
||||
if ent ~= ply or self.part_data.CollideWithOwner then
|
||||
ent:SetArmor(math.min(ent:Armor() + self.part_data.Damage, ent.GetMaxArmor and ent:GetMaxArmor() or 100))
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif data.HitEntity.SetArmor and data.HitEntity.Armor then
|
||||
data.HitEntity:SetArmor(math.min(data.HitEntity:Armor() + self.part_data.Damage, data.HitEntity.GetMaxArmor and data.HitEntity:GetMaxArmor() or 100))
|
||||
end
|
||||
else
|
||||
local info = DamageInfo()
|
||||
|
||||
info:SetAttacker(ply)
|
||||
info:SetInflictor(self)
|
||||
|
||||
if self.part_data.DamageType == "fire" then
|
||||
local ent = data.HitEntity
|
||||
if damage_radius > 0 then
|
||||
-- this should also use blast damage to find which entities it can damage
|
||||
for _, ent in ipairs(ents.FindInSphere(data.HitPos, damage_radius)) do
|
||||
if ent ~= self and ent:IsSolid() and hook.Run("CanProperty", ply, "ignite", ent) ~= false and (ent ~= ply or self.part_data.CollideWithOwner) then
|
||||
ent:Ignite(math.min(self.part_data.Damage, 5))
|
||||
end
|
||||
end
|
||||
elseif ent:IsSolid() and hook.Run("CanProperty", ply, "ignite", ent) ~= false then
|
||||
ent:Ignite(math.min(self.part_data.Damage, 5))
|
||||
end
|
||||
elseif self.part_data.DamageType == "explosion" then
|
||||
info:SetDamageType(damage_types.blast)
|
||||
info:SetDamage(math.Clamp(self.part_data.Damage, 0, 100000))
|
||||
util.BlastDamageInfo(info, data.HitPos, damage_radius)
|
||||
else
|
||||
info:SetDamageForce(data.OurOldVelocity)
|
||||
info:SetDamagePosition(data.HitPos)
|
||||
info:SetDamage(math.min(self.part_data.Damage, 100000))
|
||||
info:SetDamageType(damage_types[self.part_data.DamageType] or damage_types.generic)
|
||||
|
||||
if damage_radius > 0 then
|
||||
for _, ent in ipairs(ents.FindInSphere(data.HitPos, damage_radius)) do
|
||||
if ent ~= ply or self.part_data.CollideWithOwner then
|
||||
ent:TakeDamageInfo(info)
|
||||
end
|
||||
end
|
||||
else
|
||||
data.HitEntity:TakeDamageInfo(info)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.part_data.RemoveOnCollide then
|
||||
timer.Simple(0, function()
|
||||
SafeRemoveEntity(self)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if IsValid(self.pac_projectile_owner) then
|
||||
local ply = self.pac_projectile_owner
|
||||
ply.pac_projectiles = ply.pac_projectiles or {}
|
||||
ply.pac_projectiles[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scripted_ents.Register(ENT, ENT.ClassName)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
for key, ent in pairs(ents.FindByClass("pac_projectile")) do
|
||||
ent:Remove()
|
||||
end
|
||||
|
||||
util.AddNetworkString("pac_projectile")
|
||||
util.AddNetworkString("pac_projectile_attach")
|
||||
|
||||
net.Receive("pac_projectile", function(len, ply)
|
||||
if not enable:GetBool() then return end
|
||||
|
||||
pace.suppress_prop_spawn = true
|
||||
if hook.Run("PlayerSpawnProp", ply, "models/props_junk/popcan01a.mdl") == false then
|
||||
pace.suppress_prop_spawn = nil
|
||||
return
|
||||
end
|
||||
pace.suppress_prop_spawn = nil
|
||||
|
||||
local pos = net.ReadVector()
|
||||
local ang = net.ReadAngle()
|
||||
local part = net.ReadTable()
|
||||
|
||||
local radius_limit = 2000
|
||||
|
||||
if pos:Distance(ply:EyePos()) > radius_limit * ply:GetModelScale() then
|
||||
local ok = false
|
||||
|
||||
for _, ent in ipairs(ents.FindInSphere(pos, radius_limit)) do
|
||||
if (ent.CPPIGetOwner and ent:CPPIGetOwner() == ply) or ent.projectile_owner == ply or ent:GetOwner() == ply then
|
||||
ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then
|
||||
pos = ply:EyePos()
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn()
|
||||
if not ply:IsValid() then return end
|
||||
|
||||
ply.pac_projectiles = ply.pac_projectiles or {}
|
||||
|
||||
local projectile_count = 0
|
||||
for ent in pairs(ply.pac_projectiles) do
|
||||
if ent:IsValid() then
|
||||
projectile_count = projectile_count + 1
|
||||
else
|
||||
ply.pac_projectiles[ent] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if projectile_count > 50 then
|
||||
pac.Message("Player ", ply, " has more than 50 projectiles spawned!")
|
||||
return
|
||||
end
|
||||
|
||||
if part.Maximum > 0 and projectile_count >= part.Maximum then
|
||||
return
|
||||
end
|
||||
|
||||
local ent = ents.Create("pac_projectile")
|
||||
SafeRemoveEntityDelayed(ent,math.Clamp(part.LifeTime, 0, 50))
|
||||
|
||||
ent:SetModel("models/props_junk/popcan01a.mdl")
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
ent:Spawn()
|
||||
|
||||
if not part.CollideWithOwner then
|
||||
ent:SetOwner(ply)
|
||||
end
|
||||
|
||||
ent:SetData(ply, pos, ang, part)
|
||||
|
||||
ent:SetPhysicsAttacker(ply)
|
||||
|
||||
if ent.CPPISetOwner then
|
||||
ent:CPPISetOwner(ply)
|
||||
end
|
||||
|
||||
net.Start("pac_projectile_attach")
|
||||
net.WriteEntity(ply)
|
||||
net.WriteInt(ent:EntIndex(), 16)
|
||||
net.WriteString(part.UniqueID)
|
||||
net.Broadcast()
|
||||
|
||||
ent.pac_projectile_uid = part.UniqueID
|
||||
|
||||
ply.pac_projectiles[ent] = ent
|
||||
|
||||
ent.pac_projectile_owner = ply
|
||||
end
|
||||
|
||||
if part.Delay == 0 then
|
||||
spawn()
|
||||
else
|
||||
timer.Simple(part.Delay, spawn)
|
||||
end
|
||||
end)
|
||||
end
|
||||
Reference in New Issue
Block a user