diff --git a/QuickStackToBag/Lua/Autorun/init.lua b/QuickStackToBag/Lua/Autorun/init.lua index 2bda458..feb0f99 100644 --- a/QuickStackToBag/Lua/Autorun/init.lua +++ b/QuickStackToBag/Lua/Autorun/init.lua @@ -21,6 +21,7 @@ MyModGlobal = { STACK_TO_CURSOR = Keys.G, LOOT = Keys.L, SONAR = Keys.X, + AOEPICKUP = Keys.Y, NESTED_CONTAINERS = true, DEBUG_MODE = true, }, @@ -65,6 +66,7 @@ local quickunload = require("Cyka.quickunload") local quickreload= require("Cyka.quickreload") local quickloot = require("Cyka.quickloot") local sonarpinger = require("Cyka.sonarpinger") +local aoepickup = require("Cyka.aoepickup") require("Cyka.xpticker") require("Cyka.zoom") @@ -151,3 +153,8 @@ Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptab if not PlayerInput.KeyHit(MyModGlobal.CONFIG.SONAR) then return end sonarpinger.tryPing() end, Hook.HookMethodType.After) + +Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable) + if not PlayerInput.KeyHit(MyModGlobal.CONFIG.AOEPICKUP) then return end + aoepickup.tryAoePickup() +end, Hook.HookMethodType.After) diff --git a/QuickStackToBag/Lua/Cyka/aoepickup.lua b/QuickStackToBag/Lua/Cyka/aoepickup.lua new file mode 100644 index 0000000..ce80f98 --- /dev/null +++ b/QuickStackToBag/Lua/Cyka/aoepickup.lua @@ -0,0 +1,144 @@ +-- luacheck: globals Item Vector2 Character MyModGlobal +-- luacheck: max line length 420 +local dump = require("Cyka.dump") +local quickstack = require("Cyka.quickstack") + +---@class ItemLocation +---@field item Barotrauma.Item +---@field distance number + +---@param A Vector2 +---@param B Vector2 +---@return number +local function getDistanceQuick(A, B) + return math.abs(A.X - B.X) + math.abs(A.Y - B.Y) +end + +---@param source Vector2 +---@param distanceThreshold number +---@return ItemLocation[] +local function getNearbyItems(source, distanceThreshold) + -- local log = {} + ---@type ItemLocation[] + local items = {} + local whitelistedComponents = "Holdable,Pickable,Wearable" + local blacklistedComponents = "ConnectionPanel" + for item in Item.ItemList do + -- log[#log + 1] = string.format("Checking item %s", tostring(item)) + local distance, tags, hasAnyOfComponent + ---@cast item Barotrauma.Item + local parentInventory = item.ParentInventory + if parentInventory then + -- log[#log + 1] = string.format("Item %s is in an inventory", tostring(item)) + goto continue + end + tags = tostring(item.Tags) + if not string.find(tags, "item") then + -- log[#log + 1] = string.format("Item %s is not an item (but a structure) - %s", tostring(item), tags) + goto continue + end + for component in item.Components do + -- For some God forsaken reason this does not work + -- Not that it classifies the incorrect items + -- But it just literally does not work + -- The code does not execute + -- Some of the items vanish into thin air, as if they never existed + -- I have no idea why + -- So we'll do this in 2 steps... + -- if string.find(blacklistedComponents, component.Name) then + -- log[#log + 1] = string.format("Item %s has blacklisted component %s - %s", tostring(item), component.Name, component.Name) + -- goto continue + -- end + if string.find(whitelistedComponents, component.Name) then + hasAnyOfComponent = true + break + end + end + if not hasAnyOfComponent then + -- log[#log + 1] = string.format("Item %s is not %s", tostring(item), whitelistedComponents) + goto continue + end + + distance = getDistanceQuick(item.WorldPosition, source) + if distance > distanceThreshold then + -- log[#log + 1] = string.format("Item %s is too far away - %s", tostring(item), distance) + goto continue + end + items[#items + 1] = { + item = item, + distance = distance, + } + ::continue:: + end + -- print(table.concat(log, "\n")) + + table.sort(items, function(a, b) + return a.distance < b.distance + end) + + -- local str = "" + -- for _, item in pairs(items) do + -- str = str .. tostring(item.item) .. " " .. item.distance .. " " + -- local tags = tostring(item.item.Tags) + -- str = str .. tags .. " " + -- for component in item.item.Components do + -- str = str .. component.Name .. " " + -- end + -- str = str .. "---" .. "\n" + -- end + -- print(str) + + local filteredItems = {} + for _, item in pairs(items) do + for component in item.item.Components do + if string.find(blacklistedComponents, component.Name) then + goto continue + end + end + filteredItems[#filteredItems + 1] = item.item + ::continue:: + end + dump(filteredItems) + + -- str = "" + -- for _, item in pairs(filteredItems) do + -- str = str .. tostring(item) .. " " + -- local tags = tostring(item.Tags) + -- str = str .. tags .. " " + -- for component in item.Components do + -- str = str .. component.Name .. " " + -- end + -- str = str .. "---" .. "\n" + -- end + -- print(str) + + return filteredItems +end + +local function tryAoePickup() + MyModGlobal.debugPrint("tryAoePickup") + local character = Character.Controlled + if not character then + MyModGlobal.debugPrint("No controlled character") + return + end + + local itemTree, err = quickstack.tryBuildCharacterItemTree(character) + if err then + MyModGlobal.debugPrint(err) + return + end + + local distanceThreshold = 500 + local characterPos = character.WorldPosition + local nearbyItems = getNearbyItems(characterPos, distanceThreshold) + + local errors = quickstack.tryMoveItems(nearbyItems, itemTree, true) + for _, error in pairs(errors) do + MyModGlobal.debugPrint(string.format("Error moving items: %s", error)) + end +end + +return { + tryAoePickup = tryAoePickup, +}