diff --git a/.github/workflows/lua_linter.yaml b/.github/workflows/lua_linter.yaml
new file mode 100644
index 00000000..d849e9d9
--- /dev/null
+++ b/.github/workflows/lua_linter.yaml
@@ -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
\ No newline at end of file
diff --git a/.github/workflows/update_workshop.yaml b/.github/workflows/update_workshop.yaml
new file mode 100644
index 00000000..86bd29e1
--- /dev/null
+++ b/.github/workflows/update_workshop.yaml
@@ -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' }}
\ No newline at end of file
diff --git a/.glualint.json b/.glualint.json
new file mode 100644
index 00000000..9e1ebaff
--- /dev/null
+++ b/.glualint.json
@@ -0,0 +1,8 @@
+{
+ "lint_maxScopeDepth": 0,
+ "lint_shadowing": false,
+ "lint_spaceAfterComma": true,
+ "lint_ignoreFiles": [
+ "lua/entities/gmod_wire_expression2/*"
+ ]
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 07e79095..00000000
--- a/.travis.yml
+++ /dev/null
@@ -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 --
diff --git a/README.md b/README.md
index 7b77fe6d..f5c5e2fb 100644
--- a/README.md
+++ b/README.md
@@ -10,11 +10,10 @@ You can wear your outfit on any server with PAC3 and everyone should be able to
-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")
---
diff --git a/addon.json b/addon.json
index 66d8922e..6700ee30 100644
--- a/addon.json
+++ b/addon.json
@@ -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"
]
}
diff --git a/addon_develop.json b/addon_develop.json
new file mode 100644
index 00000000..5c6ab422
--- /dev/null
+++ b/addon_develop.json
@@ -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"
+ ]
+}
diff --git a/lua/pac3/core/client/parts/animation.lua b/lua/pac3/core/client/parts/animation.lua
index 4af1191a..9c2e973e 100644
--- a/lua/pac3/core/client/parts/animation.lua
+++ b/lua/pac3/core/client/parts/animation.lua
@@ -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
diff --git a/lua/pac3/core/client/parts/beam.lua b/lua/pac3/core/client/parts/beam.lua
index 835854a5..ab83b816 100644
--- a/lua/pac3/core/client/parts/beam.lua
+++ b/lua/pac3/core/client/parts/beam.lua
@@ -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)
diff --git a/lua/pac3/core/client/parts/event.lua b/lua/pac3/core/client/parts/event.lua
index ae785353..4673a301 100644
--- a/lua/pac3/core/client/parts/event.lua
+++ b/lua/pac3/core/client/parts/event.lua
@@ -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)
diff --git a/lua/pac3/core/client/parts/legacy/sound.lua b/lua/pac3/core/client/parts/legacy/sound.lua
index 4f55c169..8cd06b10 100644
--- a/lua/pac3/core/client/parts/legacy/sound.lua
+++ b/lua/pac3/core/client/parts/legacy/sound.lua
@@ -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
diff --git a/lua/pac3/core/client/parts/light.lua b/lua/pac3/core/client/parts/light.lua
index 68d651e0..36249ad3 100644
--- a/lua/pac3/core/client/parts/light.lua
+++ b/lua/pac3/core/client/parts/light.lua
@@ -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)
diff --git a/lua/pac3/core/client/parts/model.lua b/lua/pac3/core/client/parts/model.lua
index ad8e952d..d8fe5de4 100644
--- a/lua/pac3/core/client/parts/model.lua
+++ b/lua/pac3/core/client/parts/model.lua
@@ -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
diff --git a/lua/pac3/core/client/parts/model/entity.lua b/lua/pac3/core/client/parts/model/entity.lua
index af3da871..79383844 100644
--- a/lua/pac3/core/client/parts/model/entity.lua
+++ b/lua/pac3/core/client/parts/model/entity.lua
@@ -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()
diff --git a/lua/pac3/core/client/parts/particles.lua b/lua/pac3/core/client/parts/particles.lua
index 81c0f1e4..0ef3a2ce 100644
--- a/lua/pac3/core/client/parts/particles.lua
+++ b/lua/pac3/core/client/parts/particles.lua
@@ -356,7 +356,7 @@ function PART:EmitParticles(pos, ang, real_ang)
end
end
-
+
self.NextShot = pac.RealTime + self.FireDelay
end
self.FirstShot = false
diff --git a/lua/pac3/core/client/parts/proxy.lua b/lua/pac3/core/client/parts/proxy.lua
index aeebadd9..b111bd34 100644
--- a/lua/pac3/core/client/parts/proxy.lua
+++ b/lua/pac3/core/client/parts/proxy.lua
@@ -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
diff --git a/lua/pac3/core/client/parts/sound.lua b/lua/pac3/core/client/parts/sound.lua
index 304a2c44..da1f90d8 100644
--- a/lua/pac3/core/client/parts/sound.lua
+++ b/lua/pac3/core/client/parts/sound.lua
@@ -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
diff --git a/lua/pac3/core/client/parts/text.lua b/lua/pac3/core/client/parts/text.lua
index 2bfeb738..2eb6f4ca 100644
--- a/lua/pac3/core/client/parts/text.lua
+++ b/lua/pac3/core/client/parts/text.lua
@@ -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
diff --git a/lua/pac3/core/server/test_suite_backdoor.lua b/lua/pac3/core/server/test_suite_backdoor.lua
index 29411b38..9913cacf 100644
--- a/lua/pac3/core/server/test_suite_backdoor.lua
+++ b/lua/pac3/core/server/test_suite_backdoor.lua
@@ -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
diff --git a/lua/pac3/core/shared/footsteps_fix.lua b/lua/pac3/core/shared/footsteps_fix.lua
index 0b4b9410..8440073a 100644
--- a/lua/pac3/core/shared/footsteps_fix.lua
+++ b/lua/pac3/core/shared/footsteps_fix.lua
@@ -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
diff --git a/lua/pac3/core/shared/http.lua b/lua/pac3/core/shared/http.lua
index 4b1f98f5..fafc7db0 100644
--- a/lua/pac3/core/shared/http.lua
+++ b/lua/pac3/core/shared/http.lua
@@ -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)
diff --git a/lua/pac3/core/shared/util.lua b/lua/pac3/core/shared/util.lua
index 37e4d0cc..36c1e79b 100644
--- a/lua/pac3/core/shared/util.lua
+++ b/lua/pac3/core/shared/util.lua
@@ -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
diff --git a/lua/pac3/editor/client/animation_timeline.lua b/lua/pac3/editor/client/animation_timeline.lua
index ed68916d..3ef70db6 100644
--- a/lua/pac3/editor/client/animation_timeline.lua
+++ b/lua/pac3/editor/client/animation_timeline.lua
@@ -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
diff --git a/lua/pac3/editor/client/examples.lua b/lua/pac3/editor/client/examples.lua
index d6b399c2..9733103e 100644
--- a/lua/pac3/editor/client/examples.lua
+++ b/lua/pac3/editor/client/examples.lua
@@ -2555,7 +2555,7 @@ pace.example_outfits["southpark"] = {
["Duplicate"] = false,
["ClassName"] = "group",
},
- },
+ },
}
diff --git a/lua/pac3/editor/client/init.lua b/lua/pac3/editor/client/init.lua
index dbb76cc3..933d58a9 100644
--- a/lua/pac3/editor/client/init.lua
+++ b/lua/pac3/editor/client/init.lua
@@ -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
diff --git a/lua/pac3/editor/client/panels/extra_properties.lua b/lua/pac3/editor/client/panels/extra_properties.lua
index 48e9dcec..fb2283d5 100644
--- a/lua/pac3/editor/client/panels/extra_properties.lua
+++ b/lua/pac3/editor/client/panels/extra_properties.lua
@@ -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
diff --git a/lua/pac3/editor/client/parts.lua b/lua/pac3/editor/client/parts.lua
index 845c058b..65fad2a3 100644
--- a/lua/pac3/editor/client/parts.lua
+++ b/lua/pac3/editor/client/parts.lua
@@ -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()
diff --git a/lua/pac3/editor/client/wear.lua b/lua/pac3/editor/client/wear.lua
index f772ebc9..cb2c202d 100644
--- a/lua/pac3/editor/client/wear.lua
+++ b/lua/pac3/editor/client/wear.lua
@@ -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")
diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua
index 9ad43efc..f4472fb6 100644
--- a/lua/pac3/editor/server/wear.lua
+++ b/lua/pac3/editor/server/wear.lua
@@ -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 '')
+ reason = reason or ""
+
+ if not reason and allowed and istable(part) then
+ reason = string.format("Your part %q has been applied", partName or "")
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)
diff --git a/lua/pac3/extra/client/contraption.lua b/lua/pac3/extra/client/contraption.lua
index e2c17384..28c7cb42 100644
--- a/lua/pac3/extra/client/contraption.lua
+++ b/lua/pac3/extra/client/contraption.lua
@@ -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
\ No newline at end of file
+end
diff --git a/lua/pac3/extra/client/pac2_compat.lua b/lua/pac3/extra/client/pac2_compat.lua
index 57b846d3..47abe4d3 100644
--- a/lua/pac3/extra/client/pac2_compat.lua
+++ b/lua/pac3/extra/client/pac2_compat.lua
@@ -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()
diff --git a/lua/pac3/extra/server/contraption.lua b/lua/pac3/extra/server/contraption.lua
index 79664b0e..84687ca4 100644
--- a/lua/pac3/extra/server/contraption.lua
+++ b/lua/pac3/extra/server/contraption.lua
@@ -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)
diff --git a/lua/pac3/libraries/webaudio/stream.lua b/lua/pac3/libraries/webaudio/stream.lua
index c0140c86..08905795 100644
--- a/lua/pac3/libraries/webaudio/stream.lua
+++ b/lua/pac3/libraries/webaudio/stream.lua
@@ -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