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 Screenshot 2023-09-02 at 06 54 28 -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