Merge pull request #1315 from CapsAdmin/develop

update master branch to latest develop
This commit is contained in:
Techbot121 2023-10-30 10:41:54 +01:00 committed by GitHub
commit 44a8e67222
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 814 additions and 262 deletions

10
.github/workflows/lua_linter.yaml vendored Normal file
View File

@ -0,0 +1,10 @@
name: lint
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_call:
jobs:
lua-lint:
uses: FPtje/GLuaFixer/.github/workflows/glualint.yml@master

30
.github/workflows/update_workshop.yaml vendored Normal file
View File

@ -0,0 +1,30 @@
name: update_workshop
on:
push:
branches:
- develop
- master
workflow_dispatch:
jobs:
linter:
uses: CapsAdmin/pac3/.github/workflows/lua_linter.yaml@develop
update-workshop:
if: github.repository == 'CapsAdmin/pac3'
# needs: linter
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Publish to Steam Workshop
uses: vurv78/gmod-upload@v0.1.3
env:
STEAM_USERNAME: ${{ secrets.STEAM_NAME }}
STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }}
with:
id: ${{ github.ref == 'refs/heads/master' && '104691717' || '3038093543' }}
changelog: ${{ github.event.head_commit.message }}
config: ${{ github.ref == 'refs/heads/master' && 'addon.json' || 'addon_develop.json' }}

8
.glualint.json Normal file
View File

@ -0,0 +1,8 @@
{
"lint_maxScopeDepth": 0,
"lint_shadowing": false,
"lint_spaceAfterComma": true,
"lint_ignoreFiles": [
"lua/entities/gmod_wire_expression2/*"
]
}

View File

@ -1,22 +0,0 @@
sudo: false
language: c
addons:
apt:
packages:
- libc6:i386
- libstdc++6:i386
before_install:
# Download the lua
- wget https://github.com/Metastruct/gtravis/releases/download/travisbins/gluac.tar.xz
- tar -xf gluac.tar.xz
- export LD_LIBRARY_PATH=`pwd`/gluac${LD_LIBRARY_PATH:+:}${LD_LIBRARY_PATH:-}
# Set the $PATH so gluac can be executed
- export PATH=$PATH:`pwd`/gluac
- echo $PWD
script: find lua/ -iname '*.lua' -not -path 'lua/entities/gmod_wire_expression2/core/custom/*' -print0 | xargs -0 -- gluac -p --

View File

@ -10,11 +10,10 @@ You can wear your outfit on any server with PAC3 and everyone should be able to
<img width="650" alt="Screenshot 2023-09-02 at 06 54 28" src="https://github.com/CapsAdmin/pac3/assets/204157/276c7bfc-f5a9-422a-bfb6-683a26981539">
The Wiki for PAC3 can be found [here](https://wiki.pac3.info/start "PAC3 Wiki")
This addon is also on the [workshop](http://steamcommunity.com/sharedfiles/filedetails/?id=104691717 "Workshop Version")
You can join the official pac3 discord server [here](https://discord.gg/utpR3gJ "Join PAC3 Discord Server")
Some links to check out:
* [wiki](https://wiki.pac3.info/start "PAC3 Wiki")
* [steam workshop](http://steamcommunity.com/sharedfiles/filedetails/?id=104691717 "Workshop Version")
* [discord server](https://discord.gg/utpR3gJ "Join PAC3 Discord Server")
---

View File

@ -4,18 +4,19 @@
"tags" : [ "fun", "roleplay" ],
"ignore" :
[
".gitignore",
".editorconfig",
".git*",
".gitignore",
".glualint.json",
".vscode/*",
"*.txt",
"*.md",
"*.bat",
"*.sh",
"icon.jpg",
"*.7z",
"*.bat",
"*.md",
"*.md",
"*.sh",
"*.txt",
"*.yml",
"COPYING"
"COPYING",
"icon.jpg"
]
}

22
addon_develop.json Normal file
View File

@ -0,0 +1,22 @@
{
"title" : "PAC3 [develop version]",
"type" : "tool",
"tags" : [ "fun", "roleplay" ],
"ignore" :
[
".editorconfig",
".git*",
".gitignore",
".glualint.json",
".vscode/*",
"*.7z",
"*.bat",
"*.md",
"*.md",
"*.sh",
"*.txt",
"*.yml",
"COPYING",
"icon.jpg"
]
}

View File

@ -16,7 +16,7 @@ AnimStack = {
local top = self:getTop()
if top ~= part then
if top then top:OnStackStop() end
-- Remove self from stack to move to end and also prevent things from breaking because table.RemoveByValue() only removes the first instance
table.RemoveByValue(stack, part)
table.insert(stack, part)
@ -28,7 +28,7 @@ AnimStack = {
pop = function(self, part)
part:OnStackStop()
local stack = self.stack
-- Remove self from animation stack
if table.RemoveByValue(stack, part) == #stack + 1 then
-- This was the current animation so play the next in the stack

View File

@ -200,7 +200,7 @@ end
function PART:OnDraw()
local part = self.EndPoint
if self.Materialm and self.StartColorC and self.EndColorC and part:IsValid() and part.GetWorldPosition then
local pos, ang = self:GetDrawPosition()
render.SetMaterial(self.Materialm)

View File

@ -35,12 +35,12 @@ BUILDER:StartStorableVars()
BUILDER:EndStorableVars()
function PART:SetEvent(event)
local reset = (self.Arguments == "") or
local reset = (self.Arguments == "") or
(self.Arguments ~= "" and self.Event ~= "" and self.Event ~= event)
self.Event = event
self:SetWarning()
self:GetDynamicProperties(reset)
self:GetDynamicProperties(reset)
end
local function get_default(typ)
@ -95,8 +95,8 @@ function PART:GetDynamicProperties(reset_to_default)
}
local arg = tbl[key]
if arg.get() == nil or reset_to_default then
if udata.default then
if arg.get() == nil or reset_to_default then
if udata.default then
arg.set(udata.default)
else
arg.set(nil)
@ -824,8 +824,8 @@ PART.OldEvents = {
command = {
arguments = {{find = "string"}, {time = "number"}, {hide_in_eventwheel = "boolean"}},
userdata = {
{default = "change_me", editor_friendly = "CommandName"},
{default = 0.1, editor_friendly = "EventDuration"},
{default = "change_me", editor_friendly = "CommandName"},
{default = 0.1, editor_friendly = "EventDuration"},
{default = false, group = "event wheel", editor_friendly = "HideInEventWheel"}
},
nice = function(self, ent, find, time)

View File

@ -180,17 +180,19 @@ function PART:PlaySound(osnd, ovol)
snd = osnd
else
local sounds = self.Sound:Split(";")
--case 1: proper semicolon list
if #sounds > 1 then
if self.Sequential then
self.seq_index = self.seq_index or 1
snd = sounds[self.seq_index]
self.seq_index = self.seq_index + self.SequentialStep
self.seq_index = self.seq_index % #sounds
self.seq_index = self.seq_index % (#sounds+1)
if self.seq_index == 0 then self.seq_index = 1 end
else snd = table.Random(sounds) end
--case 2: one sound, which may or may not be bracket notation
elseif #sounds == 1 then
--bracket notation
@ -210,7 +212,7 @@ function PART:PlaySound(osnd, ovol)
"(%[%d-,%d-%])",self.seq_index
)
self.seq_index = self.seq_index + self.SequentialStep
local span = minmaxpath(self.Sound,"max") - minmaxpath(self.Sound,"min") + 1
if self.seq_index > minmaxpath(self.Sound,"max") then
self.seq_index = self.seq_index - span

View File

@ -1,3 +1,5 @@
local DynamicLight = DynamicLight
local BUILDER, PART = pac.PartTemplate("base_drawable")
PART.FriendlyName = "light"
@ -20,7 +22,7 @@ BUILDER:EndStorableVars()
function PART:GetLight()
if not self.light then
self.light = DynamicLight(tonumber(self.UniqueID))
self.light = DynamicLight(tonumber(self:GetPrintUniqueID(), 16))
end
self.light.decay = 0
self.light.dietime = math.huge
@ -33,14 +35,15 @@ function PART:RemoveLight()
local light = self.light
self.light = nil
-- this prevents fade out when removing the light
light.pos = Vector(9999,9999,9999)
light.pos = Vector(9999, 9999, 9999)
timer.Simple(0, function()
light.dietime = 0
end)
end
function PART:GetNiceName()
local hue = pac.ColorToNames(self:GetColor())
local color = self:GetColor()
local hue = pac.ColorToNames({r = color[1] * 255, g = color[2] * 255, b = color[3] * 255})
return hue .. " light"
end
@ -60,8 +63,6 @@ function PART:OnShow()
end
end
local DynamicLight = DynamicLight
function PART:OnDraw()
local pos, ang = self:GetDrawPosition()
self:GetLight().pos = pos
@ -75,9 +76,9 @@ end
function PART:SetColor(val)
self.Color = val
self:GetLight().r = math.Clamp(val.r*255, 0, 255)
self:GetLight().g = math.Clamp(val.g*255, 0, 255)
self:GetLight().b = math.Clamp(val.b*255, 0, 255)
self:GetLight().r = math.Clamp(val.r * 255, 0, 255)
self:GetLight().g = math.Clamp(val.g * 255, 0, 255)
self:GetLight().b = math.Clamp(val.b * 255, 0, 255)
end
function PART:SetBrightness(val)

View File

@ -606,7 +606,7 @@ function PART:ProcessModelChange()
local path = self.Model
if path:find("://", nil, true) then
if path:StartWith("objhttp") or path:StartWith("obj:http") or path:EndsWith(".obj") or self.ForceObjUrl then
if path:StartWith("objhttp") or path:StartWith("obj:http") or path:match("%.obj%p?") or self.ForceObjUrl then
path = path:gsub("^objhttp","http"):gsub("^obj:http","http")
self.loading = "downloading obj"
@ -747,8 +747,9 @@ function PART:SetAlternativeScaling(b)
end
function PART:SetScale(vec)
max_scale = GetConVar("pac_model_max_scales"):GetFloat()
largest_scale = math.max(math.abs(vec.x), math.abs(vec.y), math.abs(vec.z))
local max_scale = GetConVar("pac_model_max_scales"):GetFloat()
local largest_scale = math.max(math.abs(vec.x), math.abs(vec.y), math.abs(vec.z))
if vec and max_scale > 0 and (LocalPlayer() ~= self:GetPlayerOwner()) then --clamp for other players if they have pac_model_max_scales convar more than 0
vec = Vector(math.Clamp(vec.x, -max_scale, max_scale), math.Clamp(vec.y, -max_scale, max_scale), math.Clamp(vec.z, -max_scale, max_scale))
end

View File

@ -168,11 +168,11 @@ function PART:OnShow()
end
end
if not self.real_model then
self.real_model = ent:GetModel()
if not self.real_model then
self.real_model = ent:GetModel()
end
if not (self.old_model == self:GetModel()) or
if not (self.old_model == self:GetModel()) or
(pac.LocalHands:IsValid() and ent == pac.LocalHands
and not (self.real_model == pac.LocalHands:GetModel())) then
self.old_model = self:GetModel()

View File

@ -356,7 +356,7 @@ function PART:EmitParticles(pos, ang, real_ang)
end
end
self.NextShot = pac.RealTime + self.FireDelay
end
self.FirstShot = false

View File

@ -293,10 +293,26 @@ PART.Inputs.ftime = FrameTime
PART.Inputs.framenumber = FrameNumber
PART.Inputs.fnumber = FrameNumber
PART.Inputs.random = function(self)
return math.random()
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
@ -306,12 +322,71 @@ PART.Inputs.lerp = function(self, m, a, b)
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()
@ -556,10 +631,9 @@ PART.Inputs.pose_parameter_true = function(self, name)
if not name then return 0 end
local owner = get_owner(self)
if owner:IsValid() then
min,max = owner:GetPoseParameterRange(owner:LookupPoseParameter(name))
actual_value = min + (max - min)*(owner:GetPoseParameter(name))
return actual_value
else end
local min, max = owner:GetPoseParameterRange(owner:LookupPoseParameter(name))
return min + (max - min)*(owner:GetPoseParameter(name))
end
return 0
end
@ -912,6 +986,8 @@ end
function PART:OnHide()
self.time = nil
self.rand = nil
self.rand_id = nil
self.vec_additive = Vector()
if self.ResetVelocitiesOnHide then
@ -937,6 +1013,8 @@ end
function PART:OnShow()
self.time = nil
self.rand = nil
self.rand_id = nil
self.vec_additive = Vector()
end

View File

@ -19,7 +19,7 @@ BUILDER:StartStorableVars()
BUILDER:GetSet("MaxPitch", 0, {editor_sensitivity = 0.125})
BUILDER:SetPropertyGroup("playback")
BUILDER:GetSet("PlayCount", 1,
BUILDER:GetSet("PlayCount", 1,
{editor_onchange =
function(self, num)
self.sens = 0.25
@ -239,10 +239,8 @@ function PART:SetPath(path)
path = info.sound
end
if
not path:StartWith("http")
or not pac.resource.Download(path, function(path) load("data/" .. path) end)
if not string.StartsWith(path, "http") or not pac.resource.Download(path, function(path) load("data/" .. path) end)
then load("sound/" .. path)
end
end
@ -259,14 +257,14 @@ function PART:PlaySound(_, additiveVolumeFraction)
if not stream:IsValid() then return end
if self.Sequential then
self.seq_index = self.seq_index or 1
local basepath = self.paths[self.seq_index] or self.paths[1]
local snd = "sound/".. basepath
local cached_path = "data/pac3_cache/downloads/" .. pac.Hash(basepath) .. ".dat"
if string.find(basepath, "^http") then
snd = cached_path
end
@ -282,7 +280,7 @@ function PART:PlaySound(_, additiveVolumeFraction)
self.seq_index = self.seq_index + #self.paths
end
end
stream:SetAdditiveVolumeModifier(additiveVolumeFraction)
if self.last_stream:IsValid() and not self.Overlapping and not self.PauseOnHide then

View File

@ -13,26 +13,99 @@ local Color = Color
local BUILDER, PART = pac.PartTemplate("base_drawable")
local default_fonts = {
"BudgetLabel",
"CenterPrintText",
"ChatFont",
"ClientTitleFont",
"CloseCaption_Bold",
"CloseCaption_BoldItalic",
"CloseCaption_Italic",
"CloseCaption_Normal",
"CreditsLogo",
"CreditsOutroLogos",
"CreditsOutroText",
"CreditsText",
"Crosshairs",
"DebugFixed",
"DebugFixedSmall",
"DebugOverlay",
"Default",
"DefaultFixed",
"DefaultFixedDropShadow",
"DefaultSmall",
"DefaultUnderline",
"DefaultVerySmall",
"HDRDemoText",
"HL2MPTypeDeath",
"HudDefault",
"HudHintTextLarge",
"HudHintTextSmall",
"HudNumbers",
"HudNumbersGlow",
"HudNumbersSmall",
"HudSelectionNumbers",
"HudSelectionText",
"Marlett",
"QuickInfo",
"TargetID",
"TargetIDSmall",
"Trebuchet18",
"Trebuchet24",
"WeaponIcons",
"WeaponIconsSelected",
"WeaponIconsSmall",
"DermaDefault",
"DermaDefaultBold",
"DermaLarge",
"GModNotify",
"ScoreboardDefault",
"ScoreboardDefaultTitle",
"GModToolName",
"GModToolSubtitle",
"GModToolHelp",
"GModToolScreen",
"ContentHeader",
"GModWorldtip",
"ContentHeader",
"DefaultBold",
"TabLarge",
"Trebuchet22",
"TraitorState",
"TimeLeft",
"HealthAmmo",
"cool_small",
"cool_large",
"treb_small"
}
PART.ClassName = "text"
PART.Group = 'effects'
PART.Icon = 'icon16/text_align_center.png'
PART.Group = "effects"
PART.Icon = "icon16/text_align_center.png"
BUILDER:StartStorableVars()
:SetPropertyGroup("generic")
:PropertyOrder("Name")
:PropertyOrder("Hide")
:GetSet("Text", "")
:GetSet("Font", "default")
:GetSet("Font", "default", {enums = default_fonts})
:GetSet("Size", 1, {editor_sensitivity = 0.25})
:GetSet("DrawMode", "DrawTextOutlined", {enums = {
["draw.SimpleTextOutlined 3D2D"] = "DrawTextOutlined",
["draw.SimpleTextOutlined 2D"] = "DrawTextOutlined2D",
["surface.DrawText"] = "SurfaceText"
}})
:SetPropertyGroup("text layout")
:GetSet("HorizontalTextAlign", TEXT_ALIGN_CENTER, {enums = {["Left"] = "0", ["Center"] = "1", ["Right"] = "2"}})
:GetSet("VerticalTextAlign", TEXT_ALIGN_CENTER, {enums = {["Center"] = "1", ["Top"] = "3", ["Bottom"] = "4"}})
:GetSet("ConcatenateTextAndOverrideValue",false,{editor_friendly = "CombinedText"})
:GetSet("TextPosition","Prefix", {enums = {["Prefix"] = "Prefix", ["Postfix"] = "Postfix"}},{editor_friendly = "ConcatenateMode"})
:SetPropertyGroup("data source")
:GetSet("TextOverride", "Text", {enums = {
["Proxy value (DynamicTextValue)"] = "Proxy",
["Text"] = "Text",
["Health"] = "Health",
["Maximum Health"] = "MaxHealth",
@ -41,13 +114,38 @@ BUILDER:StartStorableVars()
["Timerx"] = "Timerx",
["CurTime"] = "CurTime",
["RealTime"] = "RealTime",
["Velocity"] = "Velocity",
["Velocity Vector"] = "VelocityVector",
["Position Vector"] = "PositionVector",
["Owner Position Vector"] = "OwnerPositionVector",
["Clip current Ammo"] = "Ammo",
["Clip Size"] = "ClipSize",
["Ammo Reserve"] = "AmmoReserve",
["Proxy value (Using DynamicTextValue)"] = "Proxy"}})
["Sequence Name"] = "SequenceName",
["Weapon Name"] = "Weapon",
["Vehicle Class"] = "VehicleClass",
["Model Name"] = "ModelName",
["Model Path"] = "ModelPath",
["Player Name"] = "PlayerName",
["Player SteamID"] = "SteamID",
["Map"] = "Map",
["Ground Surface"] = "GroundSurface",
["Ground Entity Class"] = "GroundEntityClass",
["Players"] = "Players",
["Max Players"] = "MaxPlayers",
["Difficulty"] = "GameDifficulty"
}})
:GetSet("DynamicTextValue", 0)
:GetSet("RoundingPosition", 2, {editor_onchange = function(self, num)
return math.Round(num,0)
end})
:SetPropertyGroup("orientation")
:PropertyOrder("AimPartName")
:PropertyOrder("Bone")
:PropertyOrder("Position")
:SetPropertyGroup("appearance")
BUILDER:GetSet("ForceAdditive",false, {description = "additive rendering for the surface.DrawText mode"})
BUILDER:GetSet("Outline", 0)
BUILDER:GetSet("Color", Vector(255, 255, 255), {editor_panel = "color"})
BUILDER:GetSet("Alpha", 1, {editor_sensitivity = 0.25, editor_clamp = {0, 1}})
@ -58,6 +156,22 @@ BUILDER:StartStorableVars()
return math.Clamp(num, 0, 1)
end})
BUILDER:GetSet("Translucent", true)
:SetPropertyGroup("CustomFont")
:GetSet("CreateCustomFont",false, {description = "Tries to create a custom font.\nHeavily throttled as creating fonts is an expensive process.\nSupport is limited because of the fonts' supported features and the limits of Lua strings.\nFont names include those stored in your operating system. for example: Comic Sans MS, Ink Free"})
:GetSet("CustomFont", "DermaDefault")
:GetSet("FontSize", 13)
:GetSet("FontWeight",500)
:GetSet("FontBlurSize",0)
:GetSet("FontScanLines",0)
:GetSet("FontAntialias",true)
:GetSet("FontUnderline",false)
:GetSet("FontItalic",false)
:GetSet("FontStrikeout",false)
:GetSet("FontSymbol",false)
:GetSet("FontRotary",false)
:GetSet("Shadow",false)
:GetSet("FontAdditive",false)
:GetSet("FontOutline",false)
BUILDER:EndStorableVars()
function PART:GetNiceName()
@ -101,60 +215,308 @@ function PART:SetOutlineAlpha(n)
end
function PART:SetFont(str)
if not pcall(surface_SetFont, str) then
pac.Message(Color(255,150,0),str.." Font not found! Reverting to DermaDefault!")
str = "DermaDefault"
self.UsedFont = str
if not self.CreateCustomFont then
if not pcall(surface_SetFont, str) then
if #self.Font > 20 then
self.lastwarn = self.lastwarn or CurTime()
if self.lastwarn > CurTime() + 1 then
pac.Message(Color(255,150,0),str.." Font not found! Could be custom font, trying again in 4 seconds!")
self.lastwarn = CurTime()
end
timer.Simple(4, function()
if not pcall(surface_SetFont, str) then
pac.Message(Color(255,150,0),str.." Font still not found! Reverting to DermaDefault!")
str = "DermaDefault"
self.UsedFont = str
end
end)
else
timer.Simple(5, function()
if not pcall(surface_SetFont, str) then
pac.Message(Color(255,150,0),str.." Font still not found! Reverting to DermaDefault!")
str = "DermaDefault"
self.UsedFont = str
end
end)
end
end
end
self.Font = str
self.Font = self.UsedFont
end
local lastfontcreationtime = 0
function PART:OnDraw()
local pos, ang = self:GetDrawPosition()
self:CheckFont()
if not pcall(surface_SetFont, self.UsedFont) then return end
local DisplayText = self.Text or ""
if self.TextOverride == "Text" then goto DRAW end
if self.TextOverride == "Health"then DisplayText = self:GetPlayerOwner():Health()
DisplayText = ""
if self.TextOverride == "Health" then DisplayText = self:GetRootPart():GetOwner():Health()
elseif self.TextOverride == "MaxHealth" then
DisplayText = self:GetPlayerOwner():GetMaxHealth()
DisplayText = self:GetRootPart():GetOwner():GetMaxHealth()
elseif self.TextOverride == "Ammo" then
DisplayText = self:GetPlayerOwner():GetActiveWeapon():Clip1()
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetActiveWeapon():Clip1() or ""
elseif self.TextOverride == "ClipSize" then
DisplayText = self:GetPlayerOwner():GetActiveWeapon():GetMaxClip1()
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetActiveWeapon():GetMaxClip1() or ""
elseif self.TextOverride == "AmmoReserve" then
DisplayText = self:GetPlayerOwner():GetAmmoCount(self:GetPlayerOwner():GetActiveWeapon():GetPrimaryAmmoType())
DisplayText = IsValid(self:GetPlayerOwner():GetActiveWeapon()) and self:GetPlayerOwner():GetAmmoCount(self:GetPlayerOwner():GetActiveWeapon():GetPrimaryAmmoType()) or ""
elseif self.TextOverride == "Armor" then
DisplayText = self:GetPlayerOwner():Armor()
elseif self.TextOverride == "MaxArmor" then
DisplayText = self:GetPlayerOwner():GetMaxArmor()
elseif self.TextOverride == "Timerx" then
DisplayText = ""..math.Round(CurTime() - self.time,2)
DisplayText = ""..math.Round(CurTime() - self.time,self.RoundingPosition)
elseif self.TextOverride == "CurTime" then
DisplayText = ""..math.Round(CurTime(),2)
DisplayText = ""..math.Round(CurTime(),self.RoundingPosition)
elseif self.TextOverride == "RealTime" then
DisplayText = ""..math.Round(RealTime(),2)
DisplayText = ""..math.Round(RealTime(),self.RoundingPosition)
elseif self.TextOverride == "Velocity" then
local ent = self:GetRootPart():GetOwner()
DisplayText = math.Round(ent:GetVelocity():Length(),2)
elseif self.TextOverride == "VelocityVector" then
local ent = self:GetOwner() or self:GetRootPart():GetOwner()
local vec = ent:GetVelocity()
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
elseif self.TextOverride == "PositionVector" then
local vec = self:GetDrawPosition()
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
elseif self.TextOverride == "OwnerPositionVector" then
local ent = self:GetRootPart():GetOwner()
local vec = ent:GetPos()
DisplayText = "("..math.Round(vec.x,self.RoundingPosition)..","..math.Round(vec.y,self.RoundingPosition)..","..math.Round(vec.z,self.RoundingPosition)..")"
elseif self.TextOverride == "SequenceName" then
DisplayText = self:GetRootPart():GetOwner():GetSequenceName(self:GetPlayerOwner():GetSequence())
elseif self.TextOverride == "PlayerName" then
DisplayText = self:GetPlayerOwner():GetName()
elseif self.TextOverride == "SteamID" then
DisplayText = self:GetPlayerOwner():SteamID()
elseif self.TextOverride == "ModelName" then
local path = self:GetRootPart():GetOwner():GetModel() or "null"
path = string.Split(path, "/")[#string.Split(path, "/")]
path = string.gsub(path,".mdl","")
DisplayText = path
elseif self.TextOverride == "ModelPath" then
DisplayText = self:GetPlayerOwner():GetModel()
elseif self.TextOverride == "Map" then
DisplayText = game.GetMap()
elseif self.TextOverride == "GroundSurface" then
local trace = util.TraceLine( {
start = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, 30),
endpos = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, -60 ),
filter = function(ent)
if ent == self:GetRootPart():GetOwner() or ent == self:GetPlayerOwner() then return false else return true end
end
})
if trace.Hit then
if trace.MatType == MAT_ANTLION then DisplayText = "Antlion"
elseif trace.MatType == MAT_BLOODYFLESH then DisplayText = "Bloody Flesh"
elseif trace.MatType == MAT_CONCRETE then DisplayText = "Concrete"
elseif trace.MatType == MAT_DIRT then DisplayText = "Dirt"
elseif trace.MatType == MAT_EGGSHELL then DisplayText = "Egg Shell"
elseif trace.MatType == MAT_FLESH then DisplayText = "Flesh"
elseif trace.MatType == MAT_GRATE then DisplayText = "Grate"
elseif trace.MatType == MAT_ALIENFLESH then DisplayText = "Alien Flesh"
elseif trace.MatType == MAT_CLIP then DisplayText = "Clip"
elseif trace.MatType == MAT_SNOW then DisplayText = "Snow"
elseif trace.MatType == MAT_PLASTIC then DisplayText = "Plastic"
elseif trace.MatType == MAT_METAL then DisplayText = "Metal"
elseif trace.MatType == MAT_SAND then DisplayText = "Sand"
elseif trace.MatType == MAT_FOLIAGE then DisplayText = "Foliage"
elseif trace.MatType == MAT_COMPUTER then DisplayText = "Computer"
elseif trace.MatType == MAT_SLOSH then DisplayText = "Slime"
elseif trace.MatType == MAT_TILE then DisplayText = "Tile"
elseif trace.MatType == MAT_GRASS then DisplayText = "Grass"
elseif trace.MatType == MAT_VENT then DisplayText = "Grass"
elseif trace.MatType == MAT_WOOD then DisplayText = "Wood"
elseif trace.MatType == MAT_DEFAULT then DisplayText = "Default"
elseif trace.MatType == MAT_GLASS then DisplayText = "Glass"
elseif trace.MatType == MAT_WARPSHIELD then DisplayText = "Warp Shield"
else DisplayText = "Other Surface" end
else DisplayText = "Air" end
elseif self.TextOverride == "GroundEntityClass" then
local trace = util.TraceLine( {
start = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, 30),
endpos = self:GetRootPart():GetOwner():GetPos() + Vector( 0, 0, -60 ),
filter = function(ent)
if ent == self:GetRootPart():GetOwner() or ent == self:GetPlayerOwner() then return false else return true end
end
})
if trace.Hit then
DisplayText = trace.Entity:GetClass()
end
elseif self.TextOverride == "GameDifficulty" then
local diff = game.GetSkillLevel()
if diff == 1 then DisplayText = "Easy"
elseif diff == 2 then DisplayText = "Normal"
elseif diff == 3 then DisplayText = "Hard" end
elseif self.TextOverride == "Players" then
DisplayText = #player.GetAll()
elseif self.TextOverride == "MaxPlayers" then
DisplayText = game.MaxPlayers()
elseif self.TextOverride == "Weapon" then
if IsValid(self:GetPlayerOwner():GetActiveWeapon()) then
DisplayText = self:GetPlayerOwner():GetActiveWeapon():GetClass()
else DisplayText = "unarmed" end
elseif self.TextOverride == "VehicleClass" then
if IsValid(self:GetPlayerOwner():GetVehicle()) then
DisplayText = self:GetPlayerOwner():GetVehicle():GetClass()
else DisplayText = "not driving" end
elseif self.TextOverride == "Proxy" then
--print(type(self.DynamicTextValue))
DisplayText = ""..math.Round(self.DynamicTextValue,2)
DisplayText = ""..math.Round(self.DynamicTextValue,self.RoundingPosition)
end
if self.ConcatenateTextAndOverrideValue then DisplayText = ""..self.Text..DisplayText end
if self.ConcatenateTextAndOverrideValue then
if self.TextPosition == "Prefix" then
DisplayText = ""..self.Text..DisplayText
elseif self.TextPosition == "Postfix" then
DisplayText = ""..DisplayText..self.Text
end
end
::DRAW::
if DisplayText ~= "" then
cam_Start3D(EyePos(), EyeAngles())
cam_Start3D2D(pos, ang, self.Size)
local oldState = DisableClipping(true)
if self.DrawMode == "DrawTextOutlined" then
cam_Start3D(EyePos(), EyeAngles())
cam_Start3D2D(pos, ang, self.Size)
local oldState = DisableClipping(true)
draw_SimpleTextOutlined(DisplayText, self.Font, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(1) -- MATERIAL_CULLMODE_CW
draw_SimpleTextOutlined(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(1) -- MATERIAL_CULLMODE_CW
draw_SimpleTextOutlined(DisplayText, self.Font, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
draw_SimpleTextOutlined(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
DisableClipping(oldState)
cam_End3D2D()
cam_End3D()
DisableClipping(oldState)
cam_End3D2D()
cam_End3D()
cam_Start3D(EyePos(), EyeAngles())
cam_Start3D2D(pos, ang, self.Size)
local oldState = DisableClipping(true)
draw.SimpleText(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(1) -- MATERIAL_CULLMODE_CW
draw.SimpleText(DisplayText, self.UsedFont, 0,0, self.ColorC, self.HorizontalTextAlign,self.VerticalTextAlign, self.Outline, self.OutlineColorC)
render_CullMode(0) -- MATERIAL_CULLMODE_CCW
DisableClipping(oldState)
cam_End3D2D()
cam_End3D()
elseif self.DrawMode == "SurfaceText" or self.DrawMode == "DrawTextOutlined2D" then
hook.Add("HUDPaint", "pac.DrawText"..self.UniqueID, function()
if not pcall(surface_SetFont, self.UsedFont) then return end
self:SetFont(self.UsedFont)
surface.SetTextColor(self.Color.r, self.Color.g, self.Color.b, 255*self.Alpha)
surface.SetFont(self.UsedFont)
local pos2d = self:GetDrawPosition():ToScreen()
local w, h = surface.GetTextSize(DisplayText)
if self.HorizontalTextAlign == 0 then --left
pos2d.x = pos2d.x
elseif self.HorizontalTextAlign == 1 then --center
pos2d.x = pos2d.x - w/2
elseif self.HorizontalTextAlign == 2 then --right
pos2d.x = pos2d.x - w
end
if self.VerticalTextAlign == 1 then --center
pos2d.y = pos2d.y - h/2
elseif self.VerticalTextAlign == 3 then --top
pos2d.y = pos2d.y
elseif self.VerticalTextAlign == 4 then --bottom
pos2d.y = pos2d.y - h
end
surface.SetTextPos(pos2d.x, pos2d.y)
local dist = (EyePos() - self:GetWorldPosition()):Length()
local fadestartdist = 200
local fadeenddist = 1000
if fadestartdist == 0 then fadestartdist = 0.1 end
if fadeenddist == 0 then fadeenddist = 0.1 end
if fadestartdist > fadeenddist then
local temp = fadestartdist
fadestartdist = fadeenddist
fadeenddist = temp
end
if dist < fadeenddist then
if dist < fadestartdist then
if self.DrawMode == "DrawTextOutlined2D" then
draw.SimpleTextOutlined(DisplayText, self.UsedFont, pos2d.x, pos2d.y, Color(self.Color.r,self.Color.g,self.Color.b,255*self.Alpha), TEXT_ALIGN_TOP, TEXT_ALIGN_LEFT, self.Outline, Color(self.OutlineColor.r,self.OutlineColor.g,self.OutlineColor.b, 255*self.OutlineAlpha))
elseif self.DrawMode == "SurfaceText" then
surface.DrawText(DisplayText, self.ForceAdditive)
end
else
local fade = math.pow(math.Clamp(1 - (dist-fadestartdist)/fadeenddist,0,1),3)
if self.DrawMode == "DrawTextOutlined2D" then
draw.SimpleTextOutlined(DisplayText, self.UsedFont, pos2d.x, pos2d.y, Color(self.Color.r,self.Color.g,self.Color.b,255*self.Alpha*fade), TEXT_ALIGN_TOP, TEXT_ALIGN_LEFT, self.Outline, Color(self.OutlineColor.r,self.OutlineColor.g,self.OutlineColor.b, 255*self.OutlineAlpha*fade))
elseif self.DrawMode == "SurfaceText" then
surface.SetTextColor(self.Color.r * fade, self.Color.g * fade, self.Color.b * fade)
surface.DrawText(DisplayText, true)
end
end
end
end)
end
if self.DrawMode == "DrawTextOutlined" then
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
end
else hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID) end
end
function PART:Initialize()
self:TryCreateFont()
end
function PART:CheckFont()
if self.CreateCustomFont then
lastfontcreationtime = lastfontcreationtime or 0
if lastfontcreationtime + 3 <= CurTime() then
self:TryCreateFont()
end
else
self:SetFont(self.Font)
end
end
function PART:TryCreateFont()
if "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID == self.lastcustomfont then
self.UsedFont = "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID
return
end
if self.CreateCustomFont then
local newfont = "Font_"..self.CustomFont.."_"..math.Round(self.FontSize,3).."_"..self.UniqueID
surface.CreateFont( newfont, {
font = self.CustomFont, -- Use the font-name which is shown to you by your operating system Font Viewer, not the file name
extended = self.Extended,
size = self.FontSize,
weight = self.Weight,
blursize = self.BlurSize,
scanlines = self.ScanLines,
antialias = self.Antialias,
underline = self.Underline,
italic = self.Italic,
strikeout = self.Strikeout,
symbol = self.Symbol,
rotary = self.Rotary,
shadow = self.Shadow,
additive = self.Additive,
outline = self.Outline,
} )
self:SetFont(newfont)
self.lastcustomfont = newfont
lastfontcreationtime = CurTime()
end
end
@ -162,6 +524,12 @@ function PART:OnShow()
self.time = CurTime()
end
function PART:OnHide()
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
end
function PART:OnRemove()
hook.Remove("HUDPaint", "pac.DrawText"..self.UniqueID)
end
function PART:SetText(str)
self.Text = str
end

View File

@ -8,7 +8,7 @@ util.AddNetworkString("pac3_test_suite_backdoor")
net.Receive("pac3_test_suite_backdoor", function(len, ply)
-- needs to be enabled through lua first, eg lua_run pac.test_suite_backdoor_enabled = true
if not pac.test_suite_backdoor_enabled then return end
-- need to be at least super admin
if not ply:IsSuperAdmin() then return end

View File

@ -4,11 +4,11 @@ if game.SinglePlayer() then
util.AddNetworkString('pac_footstep')
util.AddNetworkString('pac_footstep_request_state_update')
util.AddNetworkString('pac_signal_mute_footstep')
hook.Add("PlayerFootstep", "footstep_fix", function(ply, pos, _, snd, vol)
net.Start("pac_footstep_request_state_update")
net.Send(ply)
net.Start("pac_footstep")
net.WriteEntity(ply)
net.WriteVector(pos)
@ -16,7 +16,7 @@ if game.SinglePlayer() then
net.WriteFloat(vol)
net.Broadcast()
end)
net.Receive("pac_signal_mute_footstep", function(len,ply)
local b = net.ReadBool()
ply.pac_mute_footsteps = b
@ -26,8 +26,8 @@ if game.SinglePlayer() then
end)
else hook.Remove("PlayerFootstep", "pac_footstep_silence") end
end)
end
if CLIENT then

View File

@ -2,12 +2,12 @@
local CL_LIMIT, CL_LIMIT_OVERRIDE, CL_NO_CLENGTH
if CLIENT then
CL_LIMIT = CreateConVar("pac_webcontent_limit", "-1", {FCVAR_ARCHIVE}, "webcontent limit in mb, -1 = unlimited")
CL_LIMIT = CreateConVar("pac_webcontent_limit", "-1", {FCVAR_ARCHIVE}, "webcontent limit in kb, -1 = unlimited")
CL_NO_CLENGTH = CreateConVar("pac_webcontent_allow_no_content_length", "0", {FCVAR_ARCHIVE}, "allow downloads with no content length")
CL_LIMIT_OVERRIDE = CreateConVar("pac_webcontent_limit_force", "0", {FCVAR_ARCHIVE}, "Override serverside setting")
end
local SV_LIMIT = CreateConVar("sv_pac_webcontent_limit", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "webcontent limit in mb, -1 = unlimited")
local SV_LIMIT = CreateConVar("sv_pac_webcontent_limit", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "webcontent limit in kb, -1 = unlimited")
local SV_NO_CLENGTH = CreateConVar("sv_pac_webcontent_allow_no_content_length", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "allow downloads with no content length")
function pac.FixGMODUrl(url)

View File

@ -421,7 +421,7 @@ function pac.DownloadMDL(url, callback, onfail, ply)
if not found then
for i, v in pairs(files) do
if string.find(v.file_path, material_name, 1, true) or string.find(material_name, v.file_name, 1, true) then
v.file_name = material_name
table.insert(files, {file_name = material_name, buffer = v.buffer, crc = v.crc, file_path = v.file_path})
found = v.file_path
break
end
@ -597,8 +597,13 @@ function pac.DownloadMDL(url, callback, onfail, ply)
for i, data in ipairs(files) do
if data.file_name:EndsWith(".vmt") then
local proxies = data.buffer:match('("?%f[%w_]P?p?roxies%f[^%w_]"?%s*%b{})')
data.buffer = data.buffer:lower():gsub("\\", "/")
if proxies then
data.buffer = data.buffer:gsub('("?%f[%w_]proxies%f[^%w_]"?%s*%b{})', proxies)
end
if DEBUG_MDL or VERBOSE then
print(data.file_name .. ":")
end

View File

@ -632,7 +632,7 @@ do
surface.DrawTexturedRect(1+x,mat:Height() - 5,mat:Width(), mat:Height())
end
if esmat then
local ps = v:GetSize()
local x = v:GetPos() + (ps * 0.5)
@ -826,7 +826,7 @@ do
function KEYFRAME:GetRestart()
return self.restart
end
function KEYFRAME:GetData()
return self.DataTable
end
@ -1048,10 +1048,12 @@ do
timeline.frame.keyframe_scroll:InvalidateLayout()
self:Remove()
timeline.SelectKeyframe(timeline.frame.keyframe_scroll:GetCanvas():GetChildren()[#timeline.frame.keyframe_scroll:GetCanvas():GetChildren()])
-- * even if it was removed from the table it still exists for some reason
local count = #timeline.frame.keyframe_scroll:GetCanvas():GetChildren()
local offset = frameNum == count and count - 1 or count
timeline.SelectKeyframe(timeline.frame.keyframe_scroll:GetCanvas():GetChildren()[offset])
end):SetImage("icon16/application_delete.png")
menu:AddOption(L"set easing style", function()
if timeline.data.Interpolation != "linear" then
local frame = vgui.Create("DFrame")
@ -1059,7 +1061,7 @@ do
frame:Center()
frame:SetTitle("Easing styles work only with the linear interpolation type!")
frame:ShowCloseButton(false)
local button = vgui.Create("DButton", frame)
button:SetText("Okay")
button:Dock(FILL)
@ -1069,31 +1071,31 @@ do
frame:MakePopup()
return
end
local frameNum = self:GetAnimationIndex()
local frame = vgui.Create( "DFrame" )
frame:SetSize( 200, 100 )
frame:Center()
frame:SetTitle("Select easing type")
frame:MakePopup()
local combo = vgui.Create( "DComboBox", frame )
combo:SetPos( 5, 30 )
combo:Dock(FILL)
combo:SetValue("None")
for easeName, _ in pairs(eases) do
combo:AddChoice(easeName)
end
combo.OnSelect = function(sf, index, val)
self:SetEaseStyle(val)
frame:Close()
end
end):SetImage("icon16/arrow_turn_right.png")
if self:GetEaseStyle() then
menu:AddOption(L"unset easing style", function()
self:RemoveEaseStyle()
@ -1110,17 +1112,17 @@ do
self:GetParent():GetParent():InvalidateLayout() --rebuild the timeline
self:GetData().FrameRate = 1/math.max(int, 0.001) --set animation frame rate
end
function KEYFRAME:GetEaseStyle()
return self.estyle
end
function KEYFRAME:SetEaseStyle(style)
if not style then return end
self:GetData().EaseStyle = style
self.estyle = style
end
function KEYFRAME:RemoveEaseStyle()
self:GetData().EaseStyle = nil
self.estyle = nil

View File

@ -2555,7 +2555,7 @@ pace.example_outfits["southpark"] = {
["Duplicate"] = false,
["ClassName"] = "group",
},
},
},
}

View File

@ -353,7 +353,7 @@ do
end
end
if showInEditor:GetInt() == 1 then
if showInEditor:GetInt() == 1 then
local pos_3d = ply:NearestPoint(ply:EyePos() + up) + Vector(0,0,5)
local alpha = math.Clamp(pos_3d:Distance(EyePos()) * -1 + 500, 0, 500)/500
if alpha > 0 then

View File

@ -746,7 +746,7 @@ do -- event is_touching
mins = mins,
filter = ent
} )
if self.udata then
render.DrawWireframeBox( startpos, Angle( 0, 0, 0 ), mins, maxs, tr.Hit and Color(255,0,0) or Color(255,255,255), true )
end

View File

@ -119,7 +119,9 @@ function pace.OnPartSelected(part, is_selecting)
pace.SetViewPart(part)
pace.Editor:InvalidateLayout()
if pace.Editor:IsValid() then
pace.Editor:InvalidateLayout()
end
pace.SafeRemoveSpecialPanel()

View File

@ -240,7 +240,7 @@ end)
function pace.Notify(allowed, reason, name)
name = name or "???"
if allowed == true then
if allowed == true then
pac.Message(string.format('Your part %q has been applied', name))
else
chat.AddText(Color(255, 255, 0), "[PAC3] ", Color(255, 0, 0), string.format('The server rejected applying your part (%q) - %s', name, reason))
@ -258,9 +258,18 @@ do
elseif pace.IsActive() then
pac.Message("not wearing autoload outfit, editor is open")
else
pac.Message("Wearing autoload...")
pace.LoadParts("autoload")
pace.WearParts()
local autoload_file = "autoload"
local autoload_result = hook.Run("PAC3Autoload", autoload_file)
if autoload_result ~= false then
if isstring(autoload_result) then
autoload_file = autoload_result
end
pac.Message("Wearing " .. autoload_file .. "...")
pace.LoadParts(autoload_file)
pace.WearParts()
end
end
pac.RemoveHook("Think", "pac_request_outfits")

View File

@ -1,20 +1,38 @@
local next = next
local type = type
local istable = istable
local IsValid = IsValid
local tostring = tostring
local isfunction = isfunction
local ProtectedCall = ProtectedCall
pace.StreamQueue = pace.StreamQueue or {}
local frame_number = 0
local last_frame
local ERROR_COLOR = Color(228, 37, 37)
local function catchError(err)
pac.Message(ERROR_COLOR, 'Error: ', err)
pac.Message(debug.traceback())
end
timer.Create("pac_check_stream_queue", 0.1, 0, function()
if not pace.BusyStreaming and #pace.StreamQueue ~= 0 then
xpcall(pace.SubmitPart, catchError, unpack(table.remove(pace.StreamQueue)))
local item = table.remove(pace.StreamQueue)
if not item then return end
local data = item.data
local filter = item.filter
local callback = item.callback
local allowed, reason
local function submitPart()
allowed, reason = pace.SubmitPartNow(data, filter)
end
frame_number = frame_number + 1
local success = ProtectedCall(submitPart)
if not isfunction(callback) then return end
if not success then
allowed = false
reason = "Unexpected Error"
end
ProtectedCall(function()
callback(allowed, reason)
end)
end)
local function make_copy(tbl, input)
@ -22,7 +40,7 @@ local function make_copy(tbl, input)
tbl.self.UniqueID = pac.Hash(tbl.self.UniqueID .. input)
end
for key, val in pairs(tbl.children) do
for _, val in pairs(tbl.children) do
make_copy(val, input)
end
end
@ -45,7 +63,7 @@ end
pace.dupe_ents = pace.dupe_ents or {}
local uid2key = include("legacy_network_dictionary_translate.lua")
local uid2key = include("pac3/editor/server/legacy_network_dictionary_translate.lua")
local function translate_old_dupe(tableIn, target)
for key, value2 in pairs(tableIn) do
@ -85,7 +103,7 @@ duplicator.RegisterEntityModifier("pac_config", function(ply, ent, parts)
-- give source engine time
timer.Simple(0, function()
for uid, data in pairs(parts) do
for _, data in pairs(parts) do
if istable(data.part) then
make_copy(data.part, id)
@ -108,44 +126,47 @@ duplicator.RegisterEntityModifier("pac_config", function(ply, ent, parts)
end)
end)
function pace.SubmitPart(data, filter)
if istable(data.part) then
if last_frame == frame_number then
table.insert(pace.StreamQueue, {data, filter})
pace.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner))
return "queue"
end
function pace.SubmitPartNow(data, filter)
local part = data.part
local owner = data.owner
-- last arg "true" is pac3 only in case you need to do your checking differently from pac2
local allowed, reason = hook.Run("PrePACConfigApply", owner, data, true)
if allowed == false then return allowed, reason end
local uid = data.uid
if uid ~= false and pace.IsBanned(owner) then
return false, "you are banned from using pac"
end
-- last arg "true" is pac3 only in case you need to do your checking differnetly from pac2
local allowed, reason = hook.Run("PrePACConfigApply", data.owner, data, true)
if istable(part) then
local ent = Entity(tonumber(part.self.OwnerName) or -1)
if istable(data.part) then
local ent = Entity(tonumber(data.part.self.OwnerName) or -1)
if ent:IsValid() then
if not pace.CanPlayerModify(data.owner, ent) then
allowed = false
reason = "you are not allowed to modify this entity: " .. tostring(ent) .. " owned by: " .. tostring(ent.CPPIGetOwner and ent:CPPIGetOwner() or "world")
if not pace.CanPlayerModify(owner, ent) then
local entOwner = ent.CPPIGetOwner and ent:CPPIGetOwner()
entOwner = tostring(entOwner or "world")
return false, "you are not allowed to modify this entity: " .. tostring(ent) .. " owned by: " .. entOwner
else
if not data.is_dupe then
ent.pac_parts = ent.pac_parts or {}
ent.pac_parts[pac.Hash(data.owner)] = data
ent.pac_parts[pac.Hash(owner)] = data
pace.dupe_ents[ent:EntIndex()] = {owner = data.owner, ent = ent}
pace.dupe_ents[ent:EntIndex()] = {owner = owner, ent = ent}
duplicator.ClearEntityModifier(ent, "pac_config")
--duplicator.StoreEntityModifier(ent, "pac_config", ent.pac_parts)
--duplicator.StoreEntityModifier(ent, "pac_config", {json = util.TableToJSON(ent.pac_parts)})
-- fresh table copy
duplicator.StoreEntityModifier(ent, "pac_config", {json = util.TableToJSON(table.Copy(ent.pac_parts))})
end
ent:CallOnRemove("pac_config", function(ent)
if ent.pac_parts then
for _, data in pairs(ent.pac_parts) do
if istable(data.part) then
data.part = data.part.self.UniqueID
ent:CallOnRemove("pac_config", function(e)
if e.pac_parts then
for _, eData in pairs(e.pac_parts) do
if istable(eData.part) then
eData.part = eData.part.self.UniqueID
end
pace.RemovePart(data)
pace.RemovePart(eData)
end
end
end)
@ -153,23 +174,17 @@ function pace.SubmitPart(data, filter)
end
end
if data.uid ~= false then
if allowed == false then return allowed, reason end
if pace.IsBanned(data.owner) then return false, "you are banned from using pac" end
end
local uid = data.uid
pace.Parts[uid] = pace.Parts[uid] or {}
if istable(data.part) then
pace.Parts[uid][data.part.self.UniqueID] = data
if istable(part) then
pace.Parts[uid][part.self.UniqueID] = data
else
if data.part == "__ALL__" then
if part == "__ALL__" then
pace.Parts[uid] = {}
filter = true
for key, v in pairs(pace.dupe_ents) do
if v.owner:IsValid() and v.owner == data.owner then
if v.owner:IsValid() and v.owner == owner then
if v.ent:IsValid() and v.ent.pac_parts then
v.ent.pac_parts = nil
duplicator.ClearEntityModifier(v.ent, "pac_config")
@ -178,26 +193,8 @@ function pace.SubmitPart(data, filter)
pace.dupe_ents[key] = nil
end
elseif data.part then
pace.Parts[uid][data.part] = nil
-- this doesn't work because the unique id is different for some reason
-- use clear for now if you wanna clear a dupes outfit
--[[for key, v in pairs(pace.dupe_ents) do
if v.owner:IsValid() and v.owner == data.owner then
if v.ent:IsValid() and v.ent.pac_parts then
local id = pac.Hash(data.part .. v.ent:EntIndex())
v.ent.pac_parts[id] = nil
duplicator.ClearEntityModifier(v.ent, "pac_config")
duplicator.StoreEntityModifier(v.ent, "pac_config", v.ent.pac_parts)
return
else
pace.dupe_ents[key] = nil
end
else
pace.dupe_ents[key] = nil
end
end]]
elseif part then
pace.Parts[uid][part] = nil
end
end
@ -218,12 +215,12 @@ function pace.SubmitPart(data, filter)
end
if filter == false then
filter = data.owner
filter = owner
elseif filter == true then
local tbl = {}
for k, v in pairs(players) do
if v ~= data.owner then
for _, v in pairs(players) do
if v ~= owner then
table.insert(tbl, v)
end
end
@ -232,26 +229,28 @@ function pace.SubmitPart(data, filter)
end
if not data.server_only then
if data.owner:IsValid() then
data.player_uid = pac.Hash(data.owner)
if owner:IsValid() then
data.player_uid = pac.Hash(owner)
end
local players = filter or players
players = filter or players
if istable(players) then
for key = #players, 1, -1 do
local ply = players[key]
if not ply.pac_requested_outfits and ply ~= data.owner then
if not ply.pac_requested_outfits and ply ~= owner then
table.remove(players, key)
end
end
if pace.GlobalBans and data.owner:IsValid() then
local owner_steamid = data.owner:SteamID()
if pace.GlobalBans and owner:IsValid() then
local owner_steamid = owner:SteamID()
for key, ply in pairs(players) do
local steamid = ply:SteamID()
for var, reason in pairs(pace.GlobalBans) do
if var == steamid or istable(var) and (table.HasValue(var, steamid) or table.HasValue(var, util.CRC(ply:IPAddress():match("(.+):") or ""))) then
for var in pairs(pace.GlobalBans) do
if var == steamid or istable(var) and (table.HasValue(var, steamid) or table.HasValue(var, util.CRC(ply:IPAddress():match("(.+):") or ""))) then
table.remove(players, key)
if owner_steamid == steamid then
@ -262,7 +261,7 @@ function pace.SubmitPart(data, filter)
end
end
end
elseif type(players) == "Player" and (not players.pac_requested_outfits and players ~= data.owner) then
elseif type(players) == "Player" and (not players.pac_requested_outfits and players ~= owner) then
data.transmissionID = nil
return true
end
@ -276,19 +275,19 @@ function pace.SubmitPart(data, filter)
local bytes, err = net_write_table(data)
if not bytes then
ErrorNoHalt("[PAC3] Outfit broadcast failed for " .. tostring(data.owner) .. ": " .. tostring(err) .. '\n')
local errStr = tostring(err)
ErrorNoHalt("[PAC3] Outfit broadcast failed for " .. tostring(owner) .. ": " .. errStr .. '\n')
if data.owner and data.owner:IsValid() then
data.owner:ChatPrint('[PAC3] ERROR: Could not broadcast your outfit: ' .. tostring(err))
if owner and owner:IsValid() then
owner:ChatPrint('[PAC3] ERROR: Could not broadcast your outfit: ' .. errStr)
end
else
net.Send(players)
end
end
if istable(data.part) then
last_frame = frame_number
pace.CallHook("OnWoreOutfit", data.owner, data.part)
if istable(part) then
pace.CallHook("OnWoreOutfit", owner, part)
end
end
@ -298,33 +297,58 @@ function pace.SubmitPart(data, filter)
return true
end
-- Inserts the given part into the StreamQueue
function pace.SubmitPart(data, filter, callback)
if istable(data.part) then
pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner))
table.insert(pace.StreamQueue, {
data = data,
filter = filter,
callback = callback
})
return "queue"
end
return pace.SubmitPartNow(data, filter)
end
-- Inserts the given part into the StreamQueue, and notifies when it completes
function pace.SubmitPartNotify(data)
pace.dprint("submitted outfit %q from %s with %i number of children to set on %s", data.part.self.Name or "", data.owner:GetName(), table.Count(data.part.children), data.part.self.OwnerName or "")
local part = data.part
local partName = part.self.Name or "no name"
local allowed, reason = pace.SubmitPart(data)
pac.dprint("submitted outfit %q from %s with %i children to set on %s", partName, data.owner:GetName(), table.Count(part.children), part.self.OwnerName or "")
if data.owner:IsPlayer() then
local function callback(allowed, reason)
if allowed == "queue" then return end
if not data.owner:IsPlayer() then return end
if not reason and allowed and istable(data.part) then
reason = string.format('Your part %q has been applied', data.part.self.Name or '<unknown>')
reason = reason or ""
if not reason and allowed and istable(part) then
reason = string.format("Your part %q has been applied", partName or "<unknown>")
end
net.Start("pac_submit_acknowledged")
net.WriteBool(allowed)
net.WriteString(reason or "")
net.WriteString(data.part.self.Name or "no name")
net.WriteString(reason)
net.WriteString(partName)
net.Send(data.owner)
hook.Run("PACSubmitAcknowledged", data.owner, util.tobool(allowed), reason or "", data.part.self.Name or "no name", data)
hook.Run("PACSubmitAcknowledged", data.owner, not not allowed, reason, partName, data)
end
pace.SubmitPart(data, nil, callback)
end
function pace.RemovePart(data)
pace.dprint("%s is removed %q", data.owner and data.owner:IsValid() and data.owner:GetName(), data.part)
local part = data.part
local owner = data.owner
pac.dprint("%s is removed %q", owner and owner:IsValid() and owner:GetName(), part)
if data.part == "__ALL__" then
pace.CallHook("RemoveOutfit", data.owner)
if part == "__ALL__" then
pace.CallHook("RemoveOutfit", owner)
end
pace.SubmitPart(data, data.filter)

View File

@ -27,6 +27,10 @@ function pacx.PartToContraptionData(part, tbl)
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)
@ -39,4 +43,4 @@ function pacx.PartToContraptionData(part, tbl)
end
return tbl
end
end

View File

@ -1,5 +1,4 @@
local bones =
{
local bones = {
["pelvis"] = "valvebiped.bip01_pelvis",
["spine"] = "valvebiped.bip01_spine",
["spine 2"] = "valvebiped.bip01_spine1",
@ -326,6 +325,22 @@ 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
@ -462,21 +477,6 @@ do
return rtabs[decode_types[6](reader) - 1]
end,
}
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 reader_meta = {}
reader_meta.__index = reader_meta
function reader_meta:Next()

View File

@ -43,6 +43,16 @@ local function spawn(val,ply)
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)

View File

@ -29,7 +29,7 @@ pac.AddHook("RenderScene", "webaudio_3d", function(position, angle)
end)
webaudio.Streams.STREAM = {}
STREAM = webaudio.Streams.STREAM
local STREAM = webaudio.Streams.STREAM
STREAM.__index = STREAM
-- Identity