diff --git a/.gitignore b/.gitignore index 7067ef5..39a0086 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__ /test_release/ __test* *.bak +check_lua_versions.sh diff --git a/scripts/vscripts/alyxlib/class.lua b/scripts/vscripts/alyxlib/class.lua index 41579f9..2ddbd3d 100644 --- a/scripts/vscripts/alyxlib/class.lua +++ b/scripts/vscripts/alyxlib/class.lua @@ -1,5 +1,5 @@ --[[ - v2.3.0 + v2.3.1 https://github.com/FrostSource/alyxlib If not using `vscripts/alyxlib/core.lua`, load this file at game start using the following line: @@ -74,7 +74,7 @@ end ``` ]] -local version = "v2.3.0" +local version = "v2.3.1" require "alyxlib.storage" require "alyxlib.globals" @@ -469,15 +469,19 @@ end ---Resume the entity think function. ---@luadoc-ignore function EntityClass:ResumeThink() - self:SetContextThink("__EntityThink", function() return self:Think() end, 0) - self.IsThinking = true + if not self:IsNull() then + self:SetContextThink("__EntityThink", function() return self:Think() end, 0) + self.IsThinking = true + end end ---Pause the entity think function. ---@luadoc-ignore function EntityClass:PauseThink() - self:SetContextThink("__EntityThink", nil, 0) - self.IsThinking = false + if not self:IsNull() then + self:SetContextThink("__EntityThink", nil, 0) + self.IsThinking = false + end end ---Define a function to redirected to `output` on spawn. diff --git a/scripts/vscripts/alyxlib/controls/haptics.lua b/scripts/vscripts/alyxlib/controls/haptics.lua index 1e734e0..31022b5 100644 --- a/scripts/vscripts/alyxlib/controls/haptics.lua +++ b/scripts/vscripts/alyxlib/controls/haptics.lua @@ -1,5 +1,5 @@ --[[ - v1.0.1 + v1.0.2 https://github.com/FrostSource/alyxlib Haptic sequences allow for more complex vibrations than the one-shot pulses that the base API provides. @@ -28,7 +28,7 @@ local HapticSequenceClass = { pulseWidth_us = 0, } HapticSequenceClass.__index = HapticSequenceClass -HapticSequenceClass.version = "v1.0.1" +HapticSequenceClass.version = "v1.0.2" --- ---Start the haptic sequence on a given hand. @@ -39,6 +39,11 @@ function HapticSequenceClass:Fire(hand) hand = Entities:GetLocalPlayer():GetHMDAvatar():GetVRHand(hand) end + if not IsValidEntity(hand) then + warn("Invalid hand entity for haptic sequence!") + return + end + local ref = { increment = 0, prevTime = Time(), diff --git a/scripts/vscripts/alyxlib/controls/input.lua b/scripts/vscripts/alyxlib/controls/input.lua index 5a13faf..3a6d7d5 100644 --- a/scripts/vscripts/alyxlib/controls/input.lua +++ b/scripts/vscripts/alyxlib/controls/input.lua @@ -1,5 +1,5 @@ --[[ - v4.0.1 + v4.0.2 https://github.com/FrostSource/alyxlib Simplifies the tracking of digital action presses/releases and analog values. @@ -15,7 +15,7 @@ ---@class Input Input = {} Input.__index = Input -Input.version = "v4.0.1" +Input.version = "v4.0.2" --- ---If the input system should start automatically on player spawn. @@ -305,8 +305,8 @@ function Input:ListenToButton(kind, hand, button, presses, callback, context) kind = kind, multiple_press_count = 0, - press_time = -1, - release_time = 0, + press_time = vlua.select(kind == "press", 0, -1), + release_time = vlua.select(kind == "release", 0, -1), prev_press_time = 0, } diff --git a/scripts/vscripts/alyxlib/debug/commands.lua b/scripts/vscripts/alyxlib/debug/commands.lua index 6db7061..fe7a812 100644 --- a/scripts/vscripts/alyxlib/debug/commands.lua +++ b/scripts/vscripts/alyxlib/debug/commands.lua @@ -1,5 +1,5 @@ --[[ - v1.1.0 + v1.1.1 https://github.com/FrostSource/alyxlib If not using `vscripts/alyxlib/init.lua`, load this file at game start using the following line: @@ -7,7 +7,7 @@ require "alyxlib.debug.commands" ]] -local version = "v1.1.0" +local version = "v1.1.1" local alyxlibCommands = {} @@ -455,11 +455,11 @@ RegisterAlyxLibCommand("ent_find_by_address", function (_, tblpart, colon, hash) local foundEnt = Debug.FindEntityByHandleString(tblpart, colon, hash) if foundEnt then - Msg("Info for " .. tostring(foundEnt).."\n") - Msg("\tClassname" .. foundEnt:GetClassname().."\n") - Msg("\tName" .. foundEnt:GetName().."\n") - Msg("\tParent" .. foundEnt:GetMoveParent().."\n") - Msg("\tModel" .. foundEnt:GetModelName()) + Msg("Info for " .. tostring(foundEnt) .."\n") + Msg("\tClassname: " .. foundEnt:GetClassname() .."\n") + Msg("\tName: " .. foundEnt:GetName().."\n") + Msg("\tParent: " .. (tostring(foundEnt:GetMoveParent() or "[none]")) .."\n") + Msg("\tModel: " .. foundEnt:GetModelName()) else Msg("Could not find any entity matching '" .. hash .. "'") end diff --git a/scripts/vscripts/alyxlib/debug/common.lua b/scripts/vscripts/alyxlib/debug/common.lua index 4549c9d..bb61c5a 100644 --- a/scripts/vscripts/alyxlib/debug/common.lua +++ b/scripts/vscripts/alyxlib/debug/common.lua @@ -1,5 +1,5 @@ --[[ - v2.1.0 + v2.2.0 https://github.com/FrostSource/alyxlib Debug utility functions. @@ -13,7 +13,7 @@ require "alyxlib.extensions.entity" require "alyxlib.math.common" Debug = {} -Debug.version = "v2.1.0" +Debug.version = "v2.2.0" --- ---Finds the first entity whose name, class or model matches `pattern`. @@ -792,9 +792,20 @@ end ---@param ent EntityHandle ---@return string function Debug.EntStr(ent) + if ent == nil then + return "[nil, nil]" + end + + if ent:IsNull() then + return "[invalid, invalid]" + end + return "[" .. ent:GetClassname() .. ", " .. ent:GetName() .. "]" end +---@diagnostic disable-next-line: lowercase-global +entstr = Debug.EntStr + --- ---Dumps a list of convars and their values to the console. --- @@ -891,4 +902,13 @@ function Debug.ToOrdinalString(n) return n .. (suffixes[lastDigit] or "th") end +--- +---Get the script name and line number of a function or traceback level. +--- +---@param f integer|function # Level or function +---@return string +function Debug.GetSourceLine(f) + return debug.getinfo(f, "S").short_src..":"..tostring(debug.getinfo(f, "l").currentline) +end + return Debug.version \ No newline at end of file diff --git a/scripts/vscripts/alyxlib/debug/controller.lua b/scripts/vscripts/alyxlib/debug/controller.lua index 86d78fa..580ef58 100644 --- a/scripts/vscripts/alyxlib/debug/controller.lua +++ b/scripts/vscripts/alyxlib/debug/controller.lua @@ -1,5 +1,5 @@ --[[ - v1.0.1 + v1.0.2 https://github.com/FrostSource/alyxlib Allows quick debugging of VR controllers. @@ -11,7 +11,7 @@ -- Used for button descriptions require "alyxlib.controls.input" -local version = "v1.0.1" +local version = "v1.0.2" Convars:RegisterCommand("alyxlib_start_print_controller_button_presses", function (_) @@ -46,6 +46,7 @@ Convars:RegisterCommand("alyxlib_start_print_controller_button_presses", functio if buttonsPressed[h][i] ~= nil then buttonsPressed[h][i] = nil Msg(desc .. " controller: [".. i .."] " .. Input:GetButtonDescription(i) .. " Released\n") + msgPrinted = true end end end diff --git a/scripts/vscripts/alyxlib/extensions/entity.lua b/scripts/vscripts/alyxlib/extensions/entity.lua index 90c93dc..c94ea6b 100644 --- a/scripts/vscripts/alyxlib/extensions/entity.lua +++ b/scripts/vscripts/alyxlib/extensions/entity.lua @@ -1,5 +1,5 @@ --[[ - v2.6.1 + v2.7.0 https://github.com/FrostSource/alyxlib Provides base entity extension methods. @@ -9,7 +9,7 @@ require "alyxlib.extensions.entity" ]] -local version = "v2.6.1" +local version = "v2.7.0" --- ---Get the entities parented to this entity. Including children of children. @@ -45,6 +45,52 @@ function CBaseEntity:GetTopChildren() return children end +--- +---Get the first child in the hierarchy that has targetname or classname. +--- +---@param name string # The name or classname to look for, supports wildcard '*' +---@return EntityHandle? +function CBaseEntity:GetChild(name) + local usePattern = name:find("%*") ~= nil + local pattern + + ---@TODO Consider moving wildcard logic to a utility function + if usePattern then + -- Escape pattern special characters, then replace '*' with '.*' + pattern = "^" .. name + :gsub("([%^%$%(%)%%%.%[%]%+%-%?])", "%%%1") + :gsub("%*", ".*") .. "$" + end + + local child = self:FirstMoveChild() + while IsValidEntity(child) do + local childName = child:GetName() + local className = child:GetClassname() + + local match = false + if usePattern then + match = childName:match(pattern) or className:match(pattern) + else + match = childName == name or className == name + end + + if match then + return child + end + + -- Check children recursively + local result = child:GetChild(name) + if result then + return result + end + + child = child:NextMovePeer() + end + + return nil +end + + --- ---Send an input to this entity. --- @@ -443,4 +489,13 @@ function CBaseAnimating:GetAttachmentNameAngles(name) return self:GetAttachmentAngles(self:ScriptLookupAttachment(name)) end +--- +---Gets the forward vector of a named attachment. +--- +---@param name string +---@return Vector +function CBaseAnimating:GetAttachmentNameForward(name) + return self:GetAttachmentForward(self:ScriptLookupAttachment(name)) +end + return version \ No newline at end of file diff --git a/scripts/vscripts/alyxlib/globals.lua b/scripts/vscripts/alyxlib/globals.lua index a4a54ab..c7acdaa 100644 --- a/scripts/vscripts/alyxlib/globals.lua +++ b/scripts/vscripts/alyxlib/globals.lua @@ -1,5 +1,5 @@ --[[ - v2.6.0 + v2.7.0 https://github.com/FrostSource/alyxlib Provides common global functions used throughout extravaganza libraries. @@ -14,7 +14,7 @@ -- These are expected by globals require 'alyxlib.utils.common' -local _version = "v2.6.0" +local _version = "v2.7.0" --- ---A registered AlyxLib addon. @@ -261,6 +261,14 @@ function IsVREnabled() return GlobalSys:CommandLineCheck('-vr') end +--- +---Gets if the game was started with `+vr_enable_fake_vr 1`. +--- +---@return boolean +function IsFakeVREnabled() + return GlobalSys:CommandLineInt("+vr_enable_fake_vr", 0) == 1 +end + --- ---Prints all arguments with spaces between instead of tabs. --- diff --git a/scripts/vscripts/alyxlib/player/core.lua b/scripts/vscripts/alyxlib/player/core.lua index 37ae577..a043d74 100644 --- a/scripts/vscripts/alyxlib/player/core.lua +++ b/scripts/vscripts/alyxlib/player/core.lua @@ -1,5 +1,5 @@ --[[ - v4.2.0 + v4.2.1 https://github.com/FrostSource/alyxlib Player script allows for more advanced player manipulation and easier @@ -100,7 +100,7 @@ require "alyxlib.globals" require "alyxlib.extensions.entity" require "alyxlib.storage" -local version = "v4.2.0" +local version = "v4.2.1" ----------------------------- -- Class extension members -- @@ -957,11 +957,16 @@ function CBasePlayer:UpdateWeaponsExistence() end end - for i = #weapons.genericpistols, 1, -1 do - local generic = weapons.genericpistols[i] - local swt = Entities:FindByName(nil, "wpnswitch_" .. generic:GetName()) - if not swt then - table.remove(weapons.genericpistols, i) + if #weapons.genericpistols > 0 then + for i = #weapons.genericpistols, 1, -1 do + local generic = weapons.genericpistols[i] + -- Server change seems to destroy weapons before this is run + if IsValidEntity(generic) then + local swt = Entities:FindByName(nil, "wpnswitch_" .. generic:GetName()) + if not swt then + table.remove(weapons.genericpistols, i) + end + end end end end diff --git a/scripts/vscripts/alyxlib/player/events.lua b/scripts/vscripts/alyxlib/player/events.lua index ed7046f..86c21c5 100644 --- a/scripts/vscripts/alyxlib/player/events.lua +++ b/scripts/vscripts/alyxlib/player/events.lua @@ -1,11 +1,11 @@ --[[ - v2.1.3 + v2.2.0 https://github.com/FrostSource/alyxlib ]] -local version = "v2.1.3" +local version = "v2.2.0" ---@class __PlayerRegisteredEventData ---@field callback function @@ -107,6 +107,10 @@ local player_weapon_to_ammotype = --- local function savePlayerData() Storage.SaveTable(Player, "PlayerItems", Player.Items) + + -- Weapons aren't re-equipped on load so we need to save this + Storage.SaveString(Player, "PlayerCurrentlyEquipped", Player.CurrentlyEquipped) + if Player and Player.LeftHand then Storage.SaveEntity(Player, "LeftWristItem", Player.LeftHand.WristItem) end @@ -117,6 +121,15 @@ end local function loadPlayerData() Player.Items = Storage.LoadTable(Player, "PlayerItems", Player.Items) + + Player.CurrentlyEquipped = Storage.LoadString(Player, "PlayerCurrentlyEquipped", Player.CurrentlyEquipped) + + if Player and Player.LeftHand then + Player.LeftHand.WristItem = Storage.LoadEntity(Player, "LeftWristItem", Player.LeftHand.WristItem) + end + if Player and Player.RightHand then + Player.RightHand.WristItem = Storage.LoadEntity(Player, "RightWristItem", Player.RightHand.WristItem) + end end ---Callback logic for every player event. @@ -648,6 +661,8 @@ end ListenToGameEvent("player_drop_resin_in_backpack", listenEventPlayerDropResinInBackpack, nil) ---@class PlayerEventWeaponSwitch : GameEventWeaponSwitch +---@field item EntityHandle|nil # The handle of the weapon being switched to or nil if no weapon. +---@field item_class string # Classname of the entity that was switched to. ---Track weapon equipped ---@param data GameEventWeaponSwitch @@ -658,10 +673,13 @@ local function listenEventWeaponSwitch(data) Player.PreviouslyEquipped = Player.CurrentlyEquipped + ---@type EntityHandle + local weaponHandle = nil + if data.item == "hand_use_controller" then Player.CurrentlyEquipped = PLAYER_WEAPON_HAND else - local weaponHandle = Entities:FindBestMatching("", data.item, Player.PrimaryHand:GetPalmPosition(), 64) + weaponHandle = Entities:FindBestMatching("", data.item, Player.PrimaryHand:GetPalmPosition(), 64) if data.item == "hlvr_weapon_energygun" then Player.CurrentlyEquipped = PLAYER_WEAPON_ENERGYGUN Player.Items.weapons.energygun = weaponHandle @@ -686,10 +704,15 @@ local function listenEventWeaponSwitch(data) Player:UpdateWeaponsExistence() + Player.PrimaryHand.ItemHeld = weaponHandle + savePlayerData() -- Registered callback - eventCallback(data.game_event_name, data) + local newdata = vlua.clone(data)--[[@as PlayerEventWeaponSwitch]] + newdata.item = weaponHandle + newdata.item_class = data.item + eventCallback(data.game_event_name, newdata) end ListenToGameEvent("weapon_switch", listenEventWeaponSwitch, nil)