From 42a64a21a447a2c325c2bb976d5812c9764b79e2 Mon Sep 17 00:00:00 2001 From: git--amade Date: Sat, 4 Oct 2025 10:55:27 +0800 Subject: [PATCH 1/2] Revise fix/archery-practice for DF52.05 --- docs/fix/archery-practice.rst | 77 ++++++++-------- fix/archery-practice.lua | 134 +++++++++++++++++++++++----- internal/control-panel/registry.lua | 4 +- 3 files changed, 155 insertions(+), 60 deletions(-) diff --git a/docs/fix/archery-practice.rst b/docs/fix/archery-practice.rst index f6124e726..097a19a10 100644 --- a/docs/fix/archery-practice.rst +++ b/docs/fix/archery-practice.rst @@ -2,63 +2,66 @@ fix/archery-practice ==================== .. dfhack-tool:: - :summary: Consolidate and remove extra ammo items to fix 'Soldier (no item)' issue. + :summary: Fix quivers and training ammo items to allow archery practice to take place. :tags: fort bugfix items -Combine ammo items inside quivers that are assigned for training to allow +Make quivers the last item in the inventory of every ranged unit currently +training and split stacks of ammo items assigned for training inside the +quivers to ensure each training unit can have more than one stack to allow archery practice to take place. Usage ----- ``fix/archery-practice`` - Combine ammo items inside quivers that are assigned for training. + Move quivers to the end of units' inventory list and split stacks of + training ammo items inside the quivers. ``fix/archery-practice -q``, ``fix/archery-practice --quiet`` - Combine ammo items inside quivers that are assigned for training. - Do not print to console. + Move quivers to the end of units' inventory list and split stacks of + training ammo items inside the quivers. Do not print to console. -This tool will combine ammo items inside the quivers of units in squads -that are currently set to train with the objective of ensuring that each -unit hold only one combined stack of ammo item assigned for training in -their quiver. Any ammo items left over after the combining operation -will be dropped on the ground. +This tool will set quivers as the last item in the inventory of units in +squads that are currently set to train as well as split ammo items inside +their quivers into multiple stacks if a quiver contains only ammo item +with a stack size of 25 or larger assigned for training. The original +training ammo item with a reduced stack size will remain in the quiver +while new ammo items split from it will be placed on the ground where +the unit is located to be picked up later. -The 'Soldier (no item)' issue ------------------------------ +Why are archers not practicing archery? +--------------------------------------- Due to a bug in the game, a unit that is scheduled to train will not be able to practice archery at the archery range when their quiver contains -more than one stack of ammo item that is assigned to them for training. -This is indicated on the unit by the 'Soldier (no item)' status. +only one stack of ammo item assigned for training. This is sometimes +indicated on the unit by the 'Soldier (no item)' status. -The issue occurs when the game assigns an ammo item with a stack size of -less than 25 to the unit, prompting the game to assign additional stacks -of ammo items to make up for the deficit. +During versions 52.03 and 52.04, the issue was the complete reverse; +units would not practice when their quivers contained more than one +stack of ammo items assigned for training. -The workaround to this issue is to ensure the squad ammo assignments -for use in training contain as few ammo items with stack sizes smaller -than 25 as possible. Since training bolts are often made from wood or -bone which are created in stacks of 5, the use of the ``combine`` tool on -ammo stockpiles is recommended to reduce the frequency of this issue -occurring, while "incomplete" stacks of ammo items that are already -picked up by training units can be managed by this tool. +Another issue in 52.05 is that units will not practice archery if their +quiver is not the last item in their inventory. -Any other stacks of ammo items inside the quiver that are not assigned -for training will not affect the unit's ability to practice archery. - -As of DF version 52.05, this bug should already be fixed. +This tool provides an interim remedy by moving quivers to the end of +every training unit's inventory list and splitting stacks of ammo items +inside their quivers to prompt the game to give them multiple stacks +of training ammo items. Limitations ----------- -Due to the very limited number of ammo items a unit's quiver might contain, -the material, quality, and maker of the items are ignored when performing -the combining operation on them. Only ammo items assigned for training will -be combined, while ammo items inside the quiver that are assigned for combat -will not be affected. +The game has a tendency to reshuffle the squad's ammo/unit pairings if +the newly split ammo items are force paired to the units holding the +original ammo item. As a compromise, the new items are placed on the +ground instead and added to the squad's training ammo assignment pool, +so that the game can distribute the items normally without causing the +pairing for ammo items already in quivers to be reshuffled. -Although this tool will consolidate ammo items inside quivers and discard -any surplus items, the training units may not immediately go for archery -practice, especially if they are still trying to collect more ammo items -that the game have assigned to them. +Although this tool would allow units to practice archery, the activity +will still be aborted once they have only one stack of training ammo +item remaining in their quivers. Practicing units will gain skill from +practice, but not the positive thought they would have gained from +having completed the activity. Once the game assigns more training +ammo items to them, they can continue practicing archery. diff --git a/fix/archery-practice.lua b/fix/archery-practice.lua index 0f79dda6a..25dd0443d 100644 --- a/fix/archery-practice.lua +++ b/fix/archery-practice.lua @@ -1,4 +1,4 @@ --- Consolidate and remove extra ammo items to fix 'Soldier (no item)' issue. +-- Fix quivers and training ammo items to allow archery practice to take place. local argparse = require("argparse") local utils = require('utils') @@ -8,7 +8,7 @@ local function GetTrainingSquads() for _, squad in ipairs(df.global.world.squads.all) do if squad.entity_id == df.global.plotinfo.group_id then if #squad.ammo.ammunition > 0 and squad.activity ~= -1 then - trainingSquads[#trainingSquads + 1] = squad + table.insert(trainingSquads, squad) end end end @@ -33,13 +33,16 @@ local function GetTrainingAmmo(quiver, squad) local containedAmmo = generalRef local ammoItem = containedAmmo and df.item.find(containedAmmo.item_id) if isTrainingAmmo(ammoItem, squad) then - trainingAmmo[#trainingAmmo + 1] = ammoItem + table.insert(trainingAmmo, ammoItem) end end end return trainingAmmo end +--[[ The following functions are commented out because the nature +-- of the bug has changed, requiring a different solution. +-- More info: https://discord.com/channels/793331351645323264/873014631315148840/1423311023254933576 local function UnassignAmmo(trainingAmmo, itemToKeep, itemsToRemove, squad, unit) local plotEqAssignedAmmo = df.global.plotinfo.equipment.items_assigned.AMMO local plotEqUnassignedAmmo = df.global.plotinfo.equipment.items_unassigned.AMMO @@ -139,35 +142,124 @@ local function ConsolidateAmmo(trainingAmmo, squad, unit) end end end +-- End of commented out functions ]] + +-- Currently, only assignment to squad is practical, as pairing ammo items with +-- units directly has a tendency to cause the game to reshuffle the pairings. +local function AssignAmmoToSquad(newItems, item, squad) + local plotEqAssignedAmmo = df.global.plotinfo.equipment.items_assigned.AMMO + local assignedAmmo + for _, ammoSpec in ipairs(squad.ammo.ammunition) do + if utils.linear_index(ammoSpec.assigned, item.id) then + assignedAmmo = ammoSpec + break + end + end + for _, newItem in ipairs(newItems) do + assignedAmmo.assigned:insert('#', newItem.id) + utils.sort_vector(assignedAmmo.assigned) + plotEqAssignedAmmo:insert('#', newItem.id) + utils.sort_vector(plotEqAssignedAmmo) + end +end + +local function SplitAmmo(item, squad, unit) + local newItems = {} + repeat + local items = dfhack.items.createItem( + unit, + dfhack.items.findType('AMMO'), + item.subtype.subtype, + item.mat_type, + item.mat_index + ) + if items then + for _, newItem in ipairs(items) do + newItem:setStackSize(5) + newItem.maker_race = item.maker_race + newItem:setQuality(item.quality) + newItem.skill_rating = item.skill_rating + newItem.maker = item.maker + newItem.masterpiece_event = item.masterpiece_event + table.insert(newItems, newItem) + end + end + item:setStackSize(item.stack_size - 5) + until item.stack_size <= 5 + AssignAmmoToSquad(newItems, item, squad) +end + +local function RemoveQuiverFromInv(unit, quiver) + local equippedQuiver + for i, v in ipairs (unit.inventory) do + -- Remove quiver only if it's not the last item in the inventory. + if v.item == quiver and i ~= (#unit.inventory - 1) then + equippedQuiver = v + end + end + if equippedQuiver then + local idx = utils.linear_index(unit.inventory, equippedQuiver) + unit.inventory:erase(idx) + return true + end + return false +end + +local function MoveQuiverToEnd(unit, quiver) + local caste = dfhack.units.getCasteRaw(unit) + local bodyPart + for i, v in ipairs(caste.body_info.body_parts) do + if v.category == 'BODY_UPPER' then + bodyPart = i + break + end + end + if bodyPart then dfhack.items.moveToInventory(quiver, unit, df.inv_item_role_type.Worn, bodyPart) end +end local function FixTrainingUnits(trainingSquads, options) - local totalTrainingAmmo = 0 - local consolidateCount = 0 + local splitAmmoCount = 0 + local fixQuiverCount = 0 for _, squad in ipairs(trainingSquads) do for _, position in ipairs(squad.positions) do - if position.occupant == -1 then goto nextPosition end - local unit = df.unit.find(df.historical_figure.find(position.occupant).unit_id) - local quiver = unit and df.item.find(position.equipment.quiver) - if quiver then - local trainingAmmo = GetTrainingAmmo(quiver, squad) - if #trainingAmmo > 1 then - if not options.quiet then - local unitName = unit and dfhack.units.getReadableName(unit) - print(('Consolidating training ammo for %s...'):format(unitName)) + if position.occupant ~= -1 then + local unit = df.unit.find(df.historical_figure.find(position.occupant).unit_id) + local quiver = unit and df.item.find(position.equipment.quiver) + if quiver then + local trainingAmmo = GetTrainingAmmo(quiver, squad) + local unitName = unit and dfhack.units.getReadableName(unit) + if #trainingAmmo == 1 then + local item = trainingAmmo[1] + -- Split ammo if it's the only training ammo item and its stack size is 25 or larger. + if item.stack_size >= 25 then + if not options.quiet then + print(('Splitting training ammo for %s...'):format(unitName)) + end + SplitAmmo(item, squad, unit) + splitAmmoCount = splitAmmoCount + 1 + end + end + if RemoveQuiverFromInv(unit, quiver) then + if not options.quiet then + print(('Moving quiver to the end for %s...'):format(unitName)) + end + MoveQuiverToEnd(unit, quiver) + fixQuiverCount = fixQuiverCount + 1 end - totalTrainingAmmo = totalTrainingAmmo + #trainingAmmo - ConsolidateAmmo(trainingAmmo, squad, unit) - consolidateCount = consolidateCount + 1 end end - ::nextPosition:: end end if not options.quiet then - if consolidateCount > 0 then - print(('%d stacks of ammo items in %d quiver(s) consolidated.'):format(totalTrainingAmmo, consolidateCount)) + if splitAmmoCount > 0 then + print(('%d stack(s) of ammo item(s) split into stacks of 5.'):format(splitAmmoCount)) + else + print('No ammo items require splitting.') + end + if fixQuiverCount > 0 then + print(('%d quiver(s) moved to the end of each unit\'s inventory list.'):format(fixQuiverCount)) else - print('No stacks of ammo items require consolidation.') + print('No inventories require sorting.') end end end diff --git a/internal/control-panel/registry.lua b/internal/control-panel/registry.lua index a03c86602..78fecb78a 100644 --- a/internal/control-panel/registry.lua +++ b/internal/control-panel/registry.lua @@ -74,8 +74,8 @@ COMMANDS_BY_IDX = { -- can be restored here once we solve issue #4292 -- {command='craft-age-wear', help_command='tweak', group='bugfix', mode='tweak', default=true, -- desc='Allows items crafted from organic materials to wear out over time.'}, - {command='fix/archery-practice', group='bugfix', mode='repeat', - desc='Consolidate ammo items inside quivers to allow archery practice to take place.', + {command='fix/archery-practice', group='bugfix', mode='repeat', default=true, + desc='Fix quivers and training ammo items to allow archery practice to take place.', params={'--time', '449', '--timeUnits', 'ticks', '--command', '[', 'fix/archery-practice', '-q', ']'}}, {command='fix/blood-del', group='bugfix', mode='run', default=true}, {command='fix/dead-units', group='bugfix', mode='repeat', default=true, From a43f3392076344765cc1d8cf3dc3bb08ac4cc064 Mon Sep 17 00:00:00 2001 From: git--amade Date: Sat, 4 Oct 2025 11:03:20 +0800 Subject: [PATCH 2/2] Updated changelog.txt --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index acfac2cf7..96eb75fd7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -31,6 +31,7 @@ Template for new versions: ## New Features ## Fixes +- `fix/archery-practice`: now splits instead of combining ammo items in quivers, and moves quivers to end of unit's inventory list ## Misc Improvements