Skip to content

Commit bb77b1e

Browse files
authored
Merge branch 'master' into symbol-unstick
2 parents 86a332e + f72ccf6 commit bb77b1e

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Template for new versions:
2828

2929
## New Tools
3030
- `fix/symbol-unstick`: unstick noble symbols that cannot be re-designated.
31+
- `resize-armor`: resize armor or clothing item to any creature size.
3132

3233
## New Features
3334

docs/resize-armor.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
resize-armor
2+
============
3+
4+
.. dfhack-tool::
5+
:summary: Resize armor and clothing.
6+
:tags: adventure fort armok gameplay items
7+
8+
Resize any armor or clothing item to suit any creature size.
9+
10+
Usage
11+
-----
12+
13+
``resize-armor [<option>]``
14+
15+
Select an armor or clothing item while in either adventure or fortress mode and
16+
run this tool to resize the item. A stockpile can be selected while in fortress
17+
mode to perform the resize operation on armor or clothing items contained in
18+
the stockpile. If race ID is not specified, the items will be resized to suit
19+
the race of the current adventurer while in adventure mode, or to suit the race
20+
of the current site's civilization while in fortress mode.
21+
22+
Options
23+
-------
24+
25+
``-r``, ``--race <id>``
26+
Specify the race ID of the target race to resize the item to.

resize-armor.lua

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
-- Resize armor and clothing.
2+
3+
local argparse = require('argparse')
4+
local utils = require('utils')
5+
6+
local function isValidItem(item)
7+
local itemTypes = {
8+
df.item_type.ARMOR,
9+
df.item_type.SHOES,
10+
df.item_type.GLOVES,
11+
df.item_type.HELM,
12+
df.item_type.PANTS,
13+
}
14+
if not utils.linear_index(itemTypes, item:getType()) or
15+
item.flags.in_job or
16+
item.flags.hostile or
17+
item.flags.removed or
18+
item.flags.dead_dwarf or
19+
item.flags.rotten or
20+
item.flags.spider_web or
21+
item.flags.construction or
22+
item.flags.encased or
23+
item.flags.trader or
24+
item.flags.garbage_collect or
25+
item.flags.forbid or
26+
item.flags.dump or
27+
item.flags.on_fire or
28+
item.flags.melt
29+
then
30+
return false
31+
end
32+
return true
33+
end
34+
35+
local function GetstockpileItems(stockpile)
36+
local stockpileItems = dfhack.buildings.getStockpileContents(stockpile)
37+
local items = {}
38+
for _, stockpileItem in ipairs(stockpileItems) do
39+
local containedItems = {}
40+
for _, generalRef in ipairs(stockpileItem.general_refs) do
41+
if df.general_ref_contains_itemst:is_instance(generalRef) then
42+
containedItems = dfhack.items.getContainedItems(stockpileItem)
43+
end
44+
end
45+
if #containedItems > 0 then
46+
for _, containedItem in ipairs(containedItems) do
47+
if isValidItem(containedItem) then
48+
table.insert(items, containedItem)
49+
end
50+
end
51+
end
52+
if #stockpileItems > 0 and isValidItem(stockpileItem) then
53+
table.insert(items, stockpileItem)
54+
end
55+
end
56+
return items
57+
end
58+
59+
local function GetRace()
60+
local race
61+
if dfhack.world.isAdventureMode() then
62+
race = dfhack.world.getAdventurer().race
63+
elseif dfhack.world.isFortressMode() then
64+
local site = dfhack.world.getCurrentSite()
65+
for _, entityLink in ipairs(site.entity_links) do
66+
local entity = df.historical_entity.find(entityLink.entity_id)
67+
if entity and entity.type == df.historical_entity_type.Civilization then
68+
race = entity.race
69+
break
70+
end
71+
end
72+
end
73+
return race
74+
end
75+
76+
local function ResizeItems(items, race)
77+
local raceName = race and dfhack.units.getRaceNamePluralById(race)
78+
local resizeCount = 0
79+
for _, item in ipairs(items) do
80+
local itemName = item and dfhack.items.getReadableDescription(item)
81+
if item:getMakerRace() ~= race then
82+
item:setMakerRace(race)
83+
item:calculateWeight()
84+
resizeCount = resizeCount + 1
85+
if #items == 1 then print(('%s resized for %s.'):format(itemName, raceName)) end
86+
elseif #items == 1 then print(('%s is already sized for %s.'):format(itemName, raceName)) end
87+
end
88+
if resizeCount > 1 then
89+
print(('%d items resized for %s.'):format(resizeCount, raceName))
90+
elseif resizeCount == 0 and #items > 1 then
91+
print(('All items are already sized for %s'):format(raceName))
92+
end
93+
end
94+
95+
local function ParseCommandLine(args)
96+
local options = {
97+
help = false,
98+
race = nil,
99+
}
100+
local positionals = argparse.processArgsGetopt(args, {
101+
{'h', 'help', handler = function() options.help = true end},
102+
{'r', 'race', hasArg = true, handler = function(arg) options.race = argparse.nonnegativeInt(arg, 'race') end},
103+
})
104+
return options
105+
end
106+
107+
local function Main(args)
108+
local options = ParseCommandLine(args)
109+
if args[1] == 'help' or options.help then
110+
print(dfhack.script_help())
111+
return
112+
end
113+
local items = {}
114+
local item = dfhack.gui.getSelectedItem(true)
115+
if item then
116+
if isValidItem(item) then
117+
table.insert(items, item)
118+
else
119+
qerror('Selected item cannot be resized.')
120+
end
121+
elseif dfhack.world.isFortressMode() then
122+
local stockpile = dfhack.gui.getSelectedStockpile(true)
123+
if stockpile and df.building_stockpilest:is_instance(stockpile) then
124+
items = GetstockpileItems(stockpile)
125+
if #items < 1 then qerror('Selected stockpile contains no items that can be resized.') end
126+
end
127+
end
128+
if #items > 0 then
129+
local race = options.race or GetRace()
130+
if not race then
131+
qerror('Unable to obtain race ID. Please specify race ID manually.')
132+
end
133+
ResizeItems(items, race)
134+
else
135+
if dfhack.world.isAdventureMode() then
136+
qerror('No item selected.')
137+
elseif dfhack.world.isFortressMode() then
138+
qerror('No item or stockpile selected.')
139+
end
140+
end
141+
end
142+
143+
if not dfhack.isMapLoaded() or (
144+
not dfhack.world.isAdventureMode() and not dfhack.world.isFortressMode()
145+
)
146+
then
147+
qerror('This script requires the game to be in adventure or fortress mode.')
148+
end
149+
150+
Main({...})

0 commit comments

Comments
 (0)