diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index afa4a6dee2..c5694a6f43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: # shared across repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -20,11 +20,11 @@ repos: args: ['--fix=lf'] - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.33.0 + rev: 0.37.4 hooks: - id: check-github-workflows - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.5 + rev: v1.5.6 hooks: - id: forbid-tabs exclude_types: @@ -34,6 +34,6 @@ repos: - json # specific to scripts: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: forbid-new-submodules diff --git a/CMakeLists.txt b/CMakeLists.txt index e9e5234b0d..a673d8298a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PATTERN "*.json" PATTERN "scripts/docs" EXCLUDE PATTERN "scripts/test" EXCLUDE + PATTERN ".github" EXCLUDE + PATTERN ".vscode" EXCLUDE ) if(BUILD_TESTS) diff --git a/add-recipe.lua b/add-recipe.lua index 222dc5511f..fe7fd63576 100644 --- a/add-recipe.lua +++ b/add-recipe.lua @@ -63,7 +63,7 @@ function addItems(category, exotic) --category: the category of items we're adding --exotic: whether to add exotic items --returns: list of item objects that were added - local known = category[1] + local known_category = category[1] local native = category[2] local all = category[3] local added = {} --as:df.itemdef[] @@ -72,16 +72,20 @@ function addItems(category, exotic) local item = item --as:df.itemdef_weaponst local subtype = item.subtype local itemOk = false + local known = known_category + -- digging implements are seperate from weapons in entity resources. + if (df.itemdef_weaponst:is_instance(item) and item.skill_melee == df.job_skill.MINING) then + known = diggers + end --check if it's a training weapon local t1, t2 = pcall(function () return item.flags.TRAINING == false end) local training = not(not t1 or t2) - --we don't want procedural items with adjectives such as "wavy spears" - --(because they don't seem to be craftable even if added) + --excludes procedural items, eg: "wavy spears" for divine origins --nor do we want known items or training items (because adding training --items seems to allow them to be made out of metals) - if (item.adjective == "" and not training and not checkKnown(known, subtype)) then + if (not item.base_flags.GENERATED and not training and not checkKnown(known, subtype)) then itemOk = true end @@ -89,10 +93,12 @@ function addItems(category, exotic) itemOk = false end - --check that the weapon we're adding is not already known to the civ as - --a digging implement so picks don't get duplicated - if (checkKnown(diggers, subtype)) then - itemOk = false + --if the weapon we're adding is a digging implement, add to diggers instead of weapons + --prevents picks from being duplicated, and puts great picks in correct category + if (df.itemdef_weaponst:is_instance(item) and item.skill_melee == df.job_skill.MINING) then + if (checkKnown(diggers, subtype)) then + itemOk = false + end end if (itemOk) then diff --git a/armoks-blessing.lua b/armoks-blessing.lua index e34cc9992e..6d57ab34eb 100644 --- a/armoks-blessing.lua +++ b/armoks-blessing.lua @@ -23,7 +23,7 @@ function brainwash_unit(unit) unit.status.current_soul.personality.traits.GREED = 25 unit.status.current_soul.personality.traits.IMMODERATION = 25 unit.status.current_soul.personality.traits.VIOLENT = 50 - unit.status.current_soul.personality.traits.PERSEVERENCE = 75 + unit.status.current_soul.personality.traits.PERSEVERANCE = 75 unit.status.current_soul.personality.traits.WASTEFULNESS = 50 unit.status.current_soul.personality.traits.DISCORD = 25 unit.status.current_soul.personality.traits.FRIENDLINESS = 75 @@ -80,7 +80,7 @@ function brainwash_unit(unit) [df.value_type.HARD_WORK]=41, [df.value_type.SACRIFICE]=41, [df.value_type.COMPETITION]=-41, - [df.value_type.PERSEVERENCE]=41, + [df.value_type.PERSEVERANCE]=41, [df.value_type.LEISURE_TIME]=-11, [df.value_type.COMMERCE]=41, [df.value_type.ROMANCE]=41, diff --git a/assign-beliefs.lua b/assign-beliefs.lua index 2a0d92cdb4..cc0de2c282 100644 --- a/assign-beliefs.lua +++ b/assign-beliefs.lua @@ -134,10 +134,6 @@ function assign(beliefs, unit, reset) for belief, level in pairs(beliefs) do assert(type(level) == "number") belief = belief:upper() - -- there's a typo in the game data - if belief == "PERSEVERANCE" then - belief = "PERSEVERENCE" - end if df.value_type[belief] then if level >= -3 and level <= 3 then local belief_value = calculate_random_belief_value(level) diff --git a/assign-facets.lua b/assign-facets.lua index 3e77b3e2e0..c91670f07c 100644 --- a/assign-facets.lua +++ b/assign-facets.lua @@ -134,10 +134,6 @@ function assign(facets, unit, reset) for facet, level in pairs(facets) do assert(type(level) == "number") facet = facet:upper() - -- there's a typo in the game data - if facet == "PERSEVERANCE" then - facet = "PERSEVERENCE" - end if df.personality_facet_type[facet] then if level >= -3 and level <= 3 then local facet_strength = calculate_random_facet_strength(level) diff --git a/assign-profile.lua b/assign-profile.lua index d1207a44bf..70ce2ac600 100644 --- a/assign-profile.lua +++ b/assign-profile.lua @@ -92,7 +92,7 @@ local scripts = { FACETS = reqscript("assign-facets"), } -local default_filename = "/hack/scripts/dwarf_profiles.json" +local default_filename = dfhack.getHackPath().."/scripts/dwarf_profiles.json" -- ------------------------------------------------- APPLY PROFILE -------------------------------------------------- -- --- Apply the given profile to a unit, erasing or resetting the unit characteristics as requested. diff --git a/autocheese.lua b/autocheese.lua index 0e9fb52215..e9bdc146ae 100644 --- a/autocheese.lua +++ b/autocheese.lua @@ -1,14 +1,12 @@ --@module = true -local ic = reqscript('idle-crafting') - ---make cheese using a specific barrel and workshop ---@param barrel df.item ---@param workshop df.building_workshopst ---@return df.job function makeCheese(barrel, workshop) ---@type df.job - local job = ic.make_job() + local job = dfhack.job.createLinked() job.job_type = df.job_type.MakeCheese local jitem = df.job_item:new() @@ -22,29 +20,17 @@ function makeCheese(barrel, workshop) dfhack.error('could not attach item') end - ic.assignToWorkshop(job, workshop) + dfhack.job.assignToWorkshop(job, workshop) return job end - - ----unit is ready to take jobs +---checks that unit can path to workshop ---@param unit df.unit +---@param workshop df.building_workshopst ---@return boolean -function unitIsAvailable(unit) - if unit.job.current_job then - return false - elseif #unit.individual_drills > 0 then - return false - elseif unit.flags1.caged or unit.flags1.chained then - return false - elseif unit.military.squad_id ~= -1 then - local squad = df.squad.find(unit.military.squad_id) - -- this lookup should never fail - ---@diagnostic disable-next-line: need-check-nil - return #squad.orders == 0 and squad.activity == -1 - end - return true +function canAccessWorkshop(unit, workshop) + local workshop_position = xyz2pos(workshop.centerx, workshop.centery, workshop.z) + return dfhack.maps.canWalkBetween(unit.pos, workshop_position) end ---check if unit can perform labor at workshop @@ -54,8 +40,8 @@ end ---@return boolean function availableLaborer(unit, unit_labor, workshop) return unit.status.labors[unit_labor] - and unitIsAvailable(unit) - and ic.canAccessWorkshop(unit, workshop) + and dfhack.units.isJobAvailable(unit) + and canAccessWorkshop(unit, workshop) end ---find unit with a particular labor enabled diff --git a/autotraining.lua b/autotraining.lua new file mode 100644 index 0000000000..386ab6bb0a --- /dev/null +++ b/autotraining.lua @@ -0,0 +1,296 @@ +-- Based on the original code by RNGStrategist (who also got some help from Uncle Danny) +--@ enable = true +--@ module = true + +local repeatUtil = require('repeat-util') +local utils=require('utils') + +local GLOBAL_KEY = "autotraining" +local MartialTraining = df.need_type['MartialTraining'] +local ignore_count = 0 + +local function get_default_state() + return { + enabled=false, + threshold=-5000, + ignored={}, + ignored_nobles={}, + training_squads = {}, + } +end + +state = state or get_default_state() + +function isEnabled() + return state.enabled +end + +-- persisting a table with numeric keys results in a json array with a huge number of null entries +-- therefore, we convert the keys to strings for persistence +local function to_persist(persistable) + local persistable_ignored = {} + for k, v in pairs(persistable) do + persistable_ignored[tostring(k)] = v + end + return persistable_ignored +end + +-- loads both from the older array format and the new string table format +local function from_persist(persistable) + if not persistable then + return + end + local ret = {} + for k, v in pairs(persistable) do + ret[tonumber(k)] = v + end + return ret +end + +function persist_state() + dfhack.persistent.saveSiteData(GLOBAL_KEY, { + enabled=state.enabled, + threshold=state.threshold, + ignored=to_persist(state.ignored), + ignored_nobles=state.ignored_nobles, + training_squads=to_persist(state.training_squads) + }) +end + +--- Load the saved state of the script +local function load_state() + -- load persistent data + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) + state.enabled = persisted_data.enabled or state.enabled + state.threshold = persisted_data.threshold or state.threshold + state.ignored = from_persist(persisted_data.ignored) or state.ignored + state.ignored_nobles = persisted_data.ignored_nobles or state.ignored_nobles + state.training_squads = from_persist(persisted_data.training_squads) or state.training_squads + return state +end + +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + state.enabled = false + return + end + -- the state changed, is a map loaded and is that map in fort mode? + if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then + -- no its isnt, so bail + return + end + -- yes it was, so: + + -- retrieve state saved in game. merge with default state so config + -- saved from previous versions can pick up newer defaults. + load_state() + if state.enabled then + start() + end + persist_state() +end + + +--###### +--Functions +--###### +local function isIgnoredNoble(unit) + local noblePos = dfhack.units.getNoblePositions(unit) + if noblePos ~= nil then + for _, position in ipairs(noblePos) do + if state.ignored_nobles[position.position.code] then + return true + end + end + end + return false +end + +---@return table +function getTrainingCandidates() + local ret = {} + ignore_count = 0 + for _, unit in ipairs(dfhack.units.getCitizens(true)) do + if not dfhack.units.isAdult(unit) then + goto next_unit + end + local need = getTrainingNeed(unit) + if not need or need.focus_level >= state.threshold then + goto next_unit + end + -- ignored units are those that would like to train but are forbidden from doing so + if state.ignored[unit.id] then + ignore_count = ignore_count + 1 + goto next_unit + end + if isIgnoredNoble(unit) then + ignore_count = ignore_count + 1 + goto next_unit + end + if unit.military.squad_id ~= -1 then + goto next_unit + end + table.insert(ret, { unit = unit, need = need.focus_level }) + ::next_unit:: + end + table.sort(ret, function (a, b) return a.need < b.need end) + return ret +end + +function getTrainingSquads() + local squads = {} + for squad_id, active in pairs(state.training_squads) do + local squad = df.squad.find(squad_id) + if active and squad then + table.insert(squads, squad) + else + -- setting to nil during iteration is permitted by lua + state.training_squads[squad_id] = nil + end + end + return squads +end + +function getTrainingNeed(unit) + if unit == nil then return nil end + local needs = unit.status.current_soul.personality.needs + for _, need in ipairs(needs) do + if need.id == MartialTraining then + return need + end + end + return nil +end + +--###### +--Main +--###### + +-- Find all training squads +-- Abort if no squads found +function checkSquads() + local squads = {} + for _, squad in ipairs(getTrainingSquads()) do + if squad.entity_id == df.global.plotinfo.group_id then + local leader = squad.positions[0].occupant + if leader ~= -1 then + table.insert(squads,squad) + end + end + end + + if #squads == 0 then + return nil + end + + return squads +end + +function addTraining(unit,good_squads) + if unit.military.squad_id ~= -1 then + for _, squad in ipairs(good_squads) do + if unit.military.squad_id == squad.id then + return true + end + end + return false + end + for _, squad in ipairs(good_squads) do + for i=1,9,1 do + if squad.positions[i].occupant == -1 then + return dfhack.military.addToSquad(unit.id,squad.id,i) + end + end + end + + return false +end + +function removeAll() + if state.training_squads == nil then return end + for _, squad in ipairs(getTrainingSquads()) do + for i=1,9,1 do + local hf = df.historical_figure.find(squad.positions[i].occupant) + if hf ~= nil then + dfhack.military.removeFromSquad(hf.unit_id) + end + end + end +end + + +function check() + local squads = checkSquads() + local intraining_count = 0 + local inque_count = 0 + if squads == nil then return end + for _,squad in ipairs(squads) do + for i=1,9,1 do + if squad.positions[i].occupant ~= -1 then + local hf = df.historical_figure.find(squad.positions[i].occupant) + if hf ~= nil then + local unit = df.unit.find(hf.unit_id) + local training_need = getTrainingNeed(unit) + if not training_need or training_need.focus_level >= state.threshold then + dfhack.military.removeFromSquad(unit.id) + end + end + end + end + end + for _, p in ipairs(getTrainingCandidates()) do + local added = addTraining(p.unit, squads) + if added then + intraining_count = intraining_count +1 + else + inque_count = inque_count +1 + end + end + print(("%s: %d training, %d waiting, and %d excluded units with training needs"): + format(GLOBAL_KEY, intraining_count, inque_count, ignore_count)) +end + +function start() + repeatUtil.scheduleEvery(GLOBAL_KEY, 1, 'days', check) +end + +function stop() + repeatUtil.cancel(GLOBAL_KEY) +end + +function enable() + state.enabled = true + persist_state() + start() +end + +function disable() + state.enabled = false + persist_state() + stop() + removeAll() +end + +if dfhack_flags.module then + return +end + +validArgs = utils.invert({ + 't' +}) + +local args = utils.processArgs({...}, validArgs) + +if dfhack_flags.enable then + if dfhack_flags.enable_state then + enable() + else + disable() + end +else + -- called on the command-line + if args.t then + state.threshold = 0-tonumber(args.t) + end + print(("autotraining is %s"):format(state.enabled and "enabled" or "disabled")) +end diff --git a/ban-cooking.lua b/ban-cooking.lua index fdb59fbe05..2254e116dd 100644 --- a/ban-cooking.lua +++ b/ban-cooking.lua @@ -80,8 +80,19 @@ funcs.booze = function() end funcs.honey = function() - local mat = dfhack.matinfo.find("CREATURE:HONEY_BEE:HONEY") - ban_cooking('honey bee honey', mat.type, mat.index, df.item_type.LIQUID_MISC, -1) + for _, c in ipairs(df.global.world.raws.creatures.all) do + for _, m in ipairs(c.material) do + if m.flags.EDIBLE_COOKED then + for _, s in ipairs(m.reaction_product.id) do + if s.value == "DRINK_MAT" then + local matinfo = dfhack.matinfo.find(c.creature_id, m.id) + ban_cooking(c.name[2] .. ' ' .. m.id, matinfo.type, matinfo.index, df.item_type.LIQUID_MISC, -1) + break + end + end + end + end + end end funcs.tallow = function() diff --git a/changelog.txt b/changelog.txt index 0678eabe68..cf07906e0c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -16,12 +16,8 @@ Template for new versions: ## New Features -- `gui/design`: add option to draw N-point stars, hollow or filled or inverted, and change the main axis to orient in any direction - ## Fixes -- `gui/design`: prevent line thickness from extending outside the map boundary - ## Misc Improvements ## Removed @@ -30,6 +26,313 @@ Template for new versions: # Future +## New Tools + +## New Features + +## Fixes +- `fix/loyaltycascade`: guard against citizens that are not historical figures and emit a warning. +- `gui/siegemanager`: fix nil index if there are no siege engines on the map + +## Misc Improvements +- `caravan`: the ``Bring goods to depot``, ``Trade``, and ``Assign items for display`` overlays now allow searching for items with non-ASCII characters in their description +- `caravan`: the ``Trade`` overlay will use the trader's appraisal skill instead of the broker's to round/obfuscate the value of items + +## Removed + +# 53.15-r2 + +## New Tools + +## New Features + +## Fixes +- `combine`: stopgap fix for incorrectly combining dyes, no longer affects dyes +- `gui/rename`: fix script error that sometimes caused the script to malfunction when started from the Launcher. + +## Misc Improvements + +## Removed + +# 53.15-r1 + +## New Tools +- `machine-toggle`: interface for toggling gear assemblies, as well as pressure plates (previously in `trackstop`) (available only if ``armok`` tools are shown) + +## New Features + +## Fixes +- `add-recipe`: fix to include incorrectly excluded recipes +- `gui/control-panel`: fixed incorrect description of deteriorate commands +- `gui/petitions`: fix deity display + +## Misc Improvements +- `trackstop`: ``pressureplate`` overlay moved to `machine-toggle` as an ``armok`` tool + +## Removed + +# 53.14-r2 + +## New Tools + +## New Features + +## Fixes + +## Misc Improvements + +## Removed +- ``gui/logcleaner``: Removed + +# 53.14-r1 + +## New Tools + +## New Features + +## Fixes +- `caravan`: fix ethics warning for wooden weapons +- `gui/control-panel`: fixed inconsistent space in command help ("tweak" -> "tweak ", like with other commands) +- `gui/siegemanager`: consistently set the initial filter to "All" +- `immortal-cravings`: also take care of immortal units with alcohol dependence + +## Misc Improvements + +## Removed + +# 53.11-r2 + +## New Tools + +## New Features + +## Fixes +- `gui/rename`: skip ``NONE`` when iterating through language name options +- `quickfort`: work orders will no longer be created with a repetition frequency of ``NONE`` + +## Misc Improvements + +## Removed + +# 53.10-r2 + +## New Tools +- `gui/logcleaner`: graphical overlay for configuring the logcleaner plugin with enable and filter toggles. + +## New Features +- `trackstop`: can now modify pressure plates; permits minecart and creature triggers to be set beyond normal sensitivity + +## Fixes +- `gui/rename`: added check for entity_id input in get_target function +- `prioritize`: Fix the overlay appearing where it should not when following a unit + +## Misc Improvements +- `gui/notify`: reduced severity of the missing nemesis records warning if no units on the map are affected. clarified wording. + +## Removed + +# 53.10-r1 + +## New Tools + +## New Features +- `gui/quickcmd`: added custom command names and option to display command output +- `gui/notify`: new notification type: missing nemesis records; displays a warning message about game corruption. + +## Fixes + +## Misc Improvements + +## Removed + +# 53.07-r1 + +## New Tools +- `fix/codex-pages`: add pages to written content that have unspecified page counts. +- `gui/keybinds`: gui for managing and saving custom keybindings + +## New Features + +## Fixes +- `empty-bin`: renamed ``--liquids`` parameter to ``--force`` and made emptying of containers (bags) with powders contingent on that parameter. Previously powders would just always get disposed. + +## Misc Improvements +- `combine`: try harder to find the currently-selected stockpile + +## Removed + +# 53.06-r1 + +## New Tools + +## New Features + +## Fixes +- `gui/design`: designating a single-level stair construction now properly follows the selected stair type. +- `gui/design`: adjusted conflicting keybinds, diagonal line reverse becoming ``R`` and bottom stair type becoming ``g``. +- `modtools/set-personality`: use correct caste trait ranges; fixes `gui/gm-unit` being unable to correctly randomize traits or set traits to caste average + +## Misc Improvements + +## Removed + +# 53.04-r1 + +## New Tools +- `gui/siegemanager`: manage your siege engines at a glance. + +## New Features +- `item`: new ``--total-quality`` option for use in conjunction with ``--min-quality`` or ``--max-quality`` to filter items according to their total quality + +## Fixes + +## Misc Improvements +- `gui/design`: can now construct reinforced walls +- `quickfort`: support for reinforced walls and bolt throwers + +## Removed + +# 53.01-r1 + +## New Tools +- `fix/symbol-unstick`: unstick noble symbols that cannot be re-designated. +- `resize-armor`: resize armor or clothing item to any creature size. + +## New Features + +## Fixes +- `autotraining`: squads once used for training then disabled now properly are treated as disabled. + +## Misc Improvements + +## Removed +- `fix/archery-practice`: removed from the control panel's bug fixes tab. + +# 52.05-r2 + +## New Tools + +## 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 + +## Removed + +# 52.05-r1 + +## New Tools +- `fix/archery-practice`: combine ammo items in units' quivers to fix 'Soldier (no item)' issue +- `gui/adv-finder`: UI for tracking historical figures and artifacts in adventure mode +- `store-owned`: task owned items to be stored in the owner's room furniture + +## New Features + +## Fixes +- `ban-cooking`: bans honey added by creatures other than vanilla honey bee +- `uniform-unstick`: added quivers, backpacks, and flasks/waterskins to uniform analysis +- `uniform-unstick`: the ``--drop`` option now only evaluates clothing as possible items to drop +- `uniform-unstick`: the ``--free`` option no longer redundantly reports an improperly assigned item when that item is removed from a uniform +- `uniform-unstick`: the ``--drop`` and ``--free`` options now only drop items which are actually in a unit's inventory +- `uniform-unstick`: the ``--all`` and ``--drop`` options, when used together, now print the separator line between each unit's report in the proper place + +## Misc Improvements +- adapt Lua tools to use new API functionality for creating and assigning jobs +- `idle-crafting`: properly interrupt interruptible (i.e. "green") social activities + +## Removed + +# 52.03-r2 + +## New Tools +- `autotraining`: new tool to assign citizens to a military squad when they need Martial Training +- `gui/autotraining`: configuration tool for autotraining +- `entomb`: allow any unit that has a corpse or body parts to be assigned a tomb zone +- `husbandry`: Automatically milk and shear animals at nearby farmer's workshops + +## New Features +- `deathcause`: added functionality to this script to fetch cause of death programatically + +## Fixes +- `ban-cooking`: will not fail trying to ban honey if the world has no honey +- `confirm`: only show pause option for pausable confirmations +- `confirm`: when editing a uniform, confirm discard of changes when exiting with Escape +- `confirm`: when removing a manager order, show correct order description when using non-100% interface setting +- `confirm`: when removing a manager order, show correct order description after prior order removal or window resize (when scrolled to bottom of order list) +- `confirm`: when removing a manager order, show specific item/job type for ammo, shield, helm, gloves, shoes, trap component, and meal orders +- `confirm`: the pause option now pauses individual confirmation types, allowing multiple different confirmations to be paused independently +- `immortal-cravings`: prioritize high-value meals, properly split of portions, and don't go eating or drinking on a full stomach +- `uniform-unstick`: no longer causes units to equip multiples of assigned items +- `caravan`: in the pedestal item assignment dialog, add new items at the end of the list of displayed items instead of at a random position +- `caravan`: in the pedestal item assignment dialog, consistently remove items from the list of displayed items + +## Misc Improvements +- `devel/hello-world`: updated to show off the new Slider widget + +## Removed + +# 52.03-r1 + +## New Tools + +## New Features + +## Fixes +- `make-legendary`: ``make-legendary all`` will no longer corrupt souls + +## Misc Improvements + +## Removed + +# 52.02-r2 + +## New Tools + +## New Features +- `gui/mod-manager`: now supports arena mode + +## Fixes +- `gui/mod-manager`: gracefully handle vanilla mods with different versions from the user's preset +- `gui/mod-manager`: hide other versions of loaded mods and unhides them when unloaded + +## Misc Improvements + +## Removed + +# 52.02-r1 + +## New Tools + +## New Features + +## Fixes +- ``embark-anyone``: validate viewscreen before using, avoids a crash + +## Misc Improvements + +## Removed + +# 52.01-r1 + +## New Tools + +## New Features + +## Fixes +- fixed references to removed ``unit.curse`` compound +- `gui/gm-unit`: remove reference to ``think_counter``, removed in v51.12 +- `gui/journal`: fix typo which caused the table of contents to always be regenerated even when not needed +- `gui/mod-manager`: gracefully handle mods with missing or broken ``info.txt`` files +- `uniform-unstick`: resolve overlap with new buttons in 51.13 + +## Misc Improvements + +## Removed + +# 51.12-r1 + ## New Tools - `deteriorate`: (reinstated) allow corpses, body parts, food, and/or damaged clothes to rot away - `modtools/moddable-gods`: (reinstated) create new deities from scratch @@ -38,16 +341,16 @@ Template for new versions: - `gui/spectate`: added "Prefer nicknamed" to the list of options - `gui/mod-manager`: when run in a loaded world, shows a list of active mods -- click to export the list to the clipboard for easy sharing or posting - `gui/blueprint`: now records zone designations +- `gui/design`: add option to draw N-point stars, hollow or filled or inverted, and change the main axis to orient in any direction ## Fixes - `starvingdead`: properly restore to correct enabled state when loading a new game that is different from the first game loaded in this session - `starvingdead`: ensure undead decay does not happen faster than the declared decay rate when saving and loading the game +- `gui/design`: prevent line thickness from extending outside the map boundary ## Misc Improvements - `remove-stress`: also applied to long-term stress, immediately removing stressed and haggard statuses -## Removed - # 51.11-r1 ## Fixes diff --git a/combine.lua b/combine.lua index 3253d28db3..cd9e2522f0 100644 --- a/combine.lua +++ b/combine.lua @@ -182,6 +182,17 @@ local function stack_type_new(type_vals) return stack_type end +local function isDye(item) + -- Dyes should not be combined as this will cause bugs when mixing them together + if item:getType() ~= df.item_type.POWDER_MISC then return false end + -- pcall guards items/materials that can't be decoded or lack the flag + local ok, is_dye = pcall(function() + local mat = dfhack.matinfo.decode(item.mat_type, item.mat_index) + return mat and mat.material.flags.IS_DYE or false + end) + return ok and is_dye or false +end + local function stacks_add_item(stockpile, stacks, stack_type, item, container) -- add an item to the matching comp_items table; based on comp_key. local comp_key = '' @@ -436,7 +447,7 @@ local function stacks_add_items(stockpile, stacks, items, container, ind) local stack_type = stacks.stack_types[type_id] -- item type in list of included types? - if stack_type and not item:isSand() and not item:isPlaster() and isValidPart(item) then + if stack_type and not item:isSand() and not item:isPlaster() and not isDye(item) and isValidPart(item) then if not isRestrictedItem(item) and item.stack_size <= stack_type.max_stack_qty then stacks_add_item(stockpile, stacks, stack_type, item, container) @@ -736,6 +747,26 @@ local function get_stockpile_here() -- return the stockpile as a table local stockpiles = {} local building = dfhack.gui.getSelectedStockpile(true) + + -- try finding the stockpile by viewed item or first item in itemlist viewsheet. + if building == nil then + local item = nil + if dfhack.gui.getSelectedItem(true) ~= nil then + item = dfhack.gui.getSelectedItem(true) + elseif tonumber(dfhack.DF_VERSION:match("^0*%.*(%d+%.%d+)")) >= 50.07 -- matchFocusString() in Commit a770a4c + and dfhack.gui.matchFocusString("dwarfmode/ViewSheets/ITEM_LIST", dfhack.gui.getDFViewscreen()) + and df.global.game.main_interface.view_sheets.open == true + and df.global.game.main_interface.view_sheets.active_sheet == df.view_sheet_type.ITEM_LIST + and #df.global.game.main_interface.view_sheets.viewing_itid > 0 + then + local itemid = df.global.game.main_interface.view_sheets.viewing_itid[0] + item = df.item.find(itemid) + end + local pos = (item) and xyz2pos(dfhack.items.getPosition(item)) or nil + building = (pos) and dfhack.buildings.findAtTile(pos) or nil + building = (df.building_stockpilest:is_instance(building)) and building or nil + end + if not building then qerror('Please select a stockpile.') end table.insert(stockpiles, building) if opts.verbose > 0 then diff --git a/confirm.lua b/confirm.lua index fb0a108ed6..5142013d1f 100644 --- a/confirm.lua +++ b/confirm.lua @@ -63,6 +63,7 @@ function ConfirmOverlay:init() } end end + self.paused_confs = {} end function ConfirmOverlay:preUpdateLayout() @@ -77,11 +78,14 @@ function ConfirmOverlay:preUpdateLayout() end function ConfirmOverlay:overlay_onupdate() - if self.paused_conf and - not dfhack.gui.matchFocusString(self.paused_conf.context, + for conf in pairs(self.paused_confs) do + if not dfhack.gui.matchFocusString(conf.context, dfhack.gui.getDFViewscreen(true)) - then - self.paused_conf = nil + then + self.paused_confs[conf] = nil + end + end + if not next(self.paused_confs) then self.overlay_onupdate_max_freq_seconds = 300 end end @@ -108,19 +112,22 @@ function ConfirmOverlay:matches_conf(conf, keys, scr) end function ConfirmOverlay:onInput(keys) - if self.paused_conf or self.simulating then + if self.simulating then return false end local scr = dfhack.gui.getDFViewscreen(true) for id, conf in pairs(specs.REGISTRY) do if specs.config.data[id].enabled and self:matches_conf(conf, keys, scr) then + if self.paused_confs[conf] then + return false + end local mouse_pos = xy2pos(dfhack.screen.getMousePos()) local propagate_fn = function(pause) if conf.on_propagate then conf.on_propagate() end if pause then - self.paused_conf = conf + self.paused_confs[conf] = true self.overlay_onupdate_max_freq_seconds = 0 end if keys._MOUSE_L then @@ -131,8 +138,9 @@ function ConfirmOverlay:onInput(keys) gui.simulateInput(scr, keys) self.simulating = false end + local pause_fn = conf.pausable and curry(propagate_fn, true) or nil dialogs.showYesNoPrompt(conf.title, utils.getval(conf.message):wrap(45), COLOR_YELLOW, - propagate_fn, nil, curry(propagate_fn, true), curry(dfhack.run_script, 'gui/confirm', tostring(conf.id))) + propagate_fn, nil, pause_fn, curry(dfhack.run_script, 'gui/confirm', tostring(conf.id))) return true end end diff --git a/deathcause.lua b/deathcause.lua index 953f36bd22..3fd62fd115 100644 --- a/deathcause.lua +++ b/deathcause.lua @@ -1,10 +1,12 @@ -- show death cause of a creature +--@ module = true local DEATH_TYPES = reqscript('gui/unit-info-viewer').DEATH_TYPES -- Gets the first corpse item at the given location -function getItemAtPosition(pos) +local function getItemAtPosition(pos) for _, item in ipairs(df.global.world.items.other.ANY_CORPSE) do + -- could this maybe be `if same_xyz(pos, item.pos) then`? if item.pos.x == pos.x and item.pos.y == pos.y and item.pos.z == pos.z then print("Automatically chose first corpse at the selected location.") return item @@ -12,11 +14,11 @@ function getItemAtPosition(pos) end end -function getRaceNameSingular(race_id) +local function getRaceNameSingular(race_id) return df.creature_raw.find(race_id).name[0] end -function getDeathStringFromCause(cause) +local function getDeathStringFromCause(cause) if cause == -1 then return "died" else @@ -24,13 +26,13 @@ function getDeathStringFromCause(cause) end end -function displayDeathUnit(unit) +-- Returns a cause of death given a unit +local function getDeathCauseFromUnit(unit) local str = unit.name.has_name and '' or 'The ' str = str .. dfhack.units.getReadableName(unit) if not dfhack.units.isDead(unit) then - print(dfhack.df2console(str) .. " is not dead yet!") - return + return str .. " is not dead yet!" end str = str .. (" %s"):format(getDeathStringFromCause(unit.counters.death_cause)) @@ -50,12 +52,12 @@ function displayDeathUnit(unit) end end - print(dfhack.df2console(str) .. '.') + return str .. '.' end -- returns the item description if the item still exists; otherwise -- returns the weapon name -function getWeaponName(item_id, subtype) +local function getWeaponName(item_id, subtype) local item = df.item.find(item_id) if not item then return df.global.world.raws.itemdefs.weapons[subtype].name @@ -63,7 +65,7 @@ function getWeaponName(item_id, subtype) return dfhack.items.getDescription(item, 0, false) end -function displayDeathEventHistFigUnit(histfig_unit, event) +local function getDeathEventHistFigUnit(histfig_unit, event) local str = ("The %s %s %s in year %d"):format( getRaceNameSingular(histfig_unit.race), dfhack.translation.translateName(dfhack.units.getVisibleName(histfig_unit)), @@ -87,11 +89,11 @@ function displayDeathEventHistFigUnit(histfig_unit, event) end end - print(dfhack.df2console(str) .. '.') + return str .. '.' end -- Returns the death event for the given histfig or nil if not found -function getDeathEventForHistFig(histfig_id) +local function getDeathEventForHistFig(histfig_id) for i = #df.global.world.history.events - 1, 0, -1 do local event = df.global.world.history.events[i] if event:getType() == df.history_event_type.HIST_FIGURE_DIED then @@ -102,17 +104,18 @@ function getDeathEventForHistFig(histfig_id) end end -function displayDeathHistFig(histfig) +-- Returns the cause of death given a histfig +local function getDeathCauseFromHistFig(histfig) local histfig_unit = df.unit.find(histfig.unit_id) if not histfig_unit then qerror("Cause of death not available") end if not dfhack.units.isDead(histfig_unit) then - print(("%s is not dead yet!"):format(dfhack.df2console(dfhack.units.getReadableName(histfig_unit)))) + return ("%s is not dead yet!"):format(dfhack.units.getReadableName(histfig_unit)) else local death_event = getDeathEventForHistFig(histfig.id) - displayDeathEventHistFigUnit(histfig_unit, death_event) + return getDeathEventHistFigUnit(histfig_unit, death_event) end end @@ -147,6 +150,19 @@ local function get_target() return selected_item.hist_figure_id, df.unit.find(selected_item.unit_id) end +-- wrapper function to take either a unit or a histfig and get the death cause +function getDeathCause(target) + if df.unit:is_instance(target) then + return getDeathCauseFromUnit(target) + else + return getDeathCauseFromHistFig(target) + end +end + +if dfhack_flags.module then + return +end + local hist_figure_id, selected_unit = get_target() if not hist_figure_id then @@ -155,7 +171,7 @@ elseif hist_figure_id == -1 then if not selected_unit then qerror("Cause of death not available") end - displayDeathUnit(selected_unit) + print(dfhack.df2console(getDeathCause(selected_unit))) else - displayDeathHistFig(df.historical_figure.find(hist_figure_id)) + print(dfhack.df2console(getDeathCause(df.historical_figure.find(hist_figure_id)))) end diff --git a/devel/export-dt-ini.lua b/devel/export-dt-ini.lua index ad4d5bfbde..5bf5eadd6c 100644 --- a/devel/export-dt-ini.lua +++ b/devel/export-dt-ini.lua @@ -318,9 +318,9 @@ address('physical_attrs',df.unit,'body','physical_attrs') address('body_size',df.unit,'appearance','body_modifiers') address('size_info',df.unit,'body','size_info','size_cur') address('size_base',df.unit,'body','size_info','size_base') -address('curse',df.unit,'curse','name') -address('curse_add_flags1',df.unit,'curse','add_tags1') -address('turn_count',df.unit,'curse','interaction','time_on_site') +address('curse',df.unit,'uwss_display_name_sing') +address('curse_add_flags1',df.unit,'uwss_add_caste_flag') +address('turn_count',df.unit,'usable_interaction','time_on_site') address('souls',df.unit,'status','souls') address('states',df.unit,'status','misc_traits') address('labors',df.unit,'status','labors') diff --git a/devel/hello-world.lua b/devel/hello-world.lua index 679bf1d52e..2af576f815 100644 --- a/devel/hello-world.lua +++ b/devel/hello-world.lua @@ -12,17 +12,27 @@ local HIGHLIGHT_PEN = dfhack.pen.parse{ HelloWorldWindow = defclass(HelloWorldWindow, widgets.Window) HelloWorldWindow.ATTRS{ - frame={w=20, h=14}, + frame={w=25, h=25}, frame_title='Hello World', autoarrange_subviews=true, - autoarrange_gap=1, + autoarrange_gap=2, + resizable=true, + resize_min={w=25, h=25}, } function HelloWorldWindow:init() + local LEVEL_OPTIONS = { + {label='Low', value=1}, + {label='Medium', value=2}, + {label='High', value=3}, + {label='Pro', value=4}, + {label='Insane', value=5}, + } + self:addviews{ widgets.Label{text={{text='Hello, world!', pen=COLOR_LIGHTGREEN}}}, widgets.HotkeyLabel{ - frame={l=0, t=0}, + frame={l=0}, label='Click me', key='CUSTOM_CTRL_A', on_activate=self:callback('toggleHighlight'), @@ -32,6 +42,28 @@ function HelloWorldWindow:init() frame={w=10, h=5}, frame_style=gui.INTERIOR_FRAME, }, + widgets.Divider{ + frame={h=1}, + frame_style_l=false, + frame_style_r=false, + }, + widgets.CycleHotkeyLabel{ + view_id='level', + frame={l=0, w=20}, + label='Level:', + key_back='CUSTOM_SHIFT_C', + key='CUSTOM_SHIFT_V', + options=LEVEL_OPTIONS, + initial_option=LEVEL_OPTIONS[1].value, + }, + widgets.Slider{ + frame={l=1}, + num_stops=#LEVEL_OPTIONS, + get_idx_fn=function() + return self.subviews.level:getOptionValue() + end, + on_change=function(idx) self.subviews.level:setOption(idx) end, + }, } end diff --git a/devel/make-dt.pl b/devel/make-dt.pl index 4bffeb0d55..9d8ba2c433 100755 --- a/devel/make-dt.pl +++ b/devel/make-dt.pl @@ -325,9 +325,9 @@ ($$$$) emit_addr 'physical_attrs',%all,'unit','body.physical_attrs'; emit_addr 'body_size',%all,'unit','appearance.body_modifiers'; emit_addr 'size_info',%all,'unit','body.size_info'; - emit_addr 'curse',%all,'unit','curse.name'; - emit_addr 'curse_add_flags1',%all,'unit','curse.add_tags1'; - emit_addr 'turn_count',%all,'unit','curse.time_on_site'; + emit_addr 'curse',%all,'unit','uwss_display_name_sing'; + emit_addr 'curse_add_flags1',%all,'unit','uwss_add_caste_flag'; + emit_addr 'turn_count',%all,'unit','usable_interaction.time_on_site'; emit_addr 'souls',%all,'unit','status.souls'; emit_addr 'states',%all,'unit','status.misc_traits'; emit_addr 'labors',%all,'unit','status.labors'; diff --git a/devel/print-event.lua b/devel/print-event.lua index f2d9a54aad..771ac55299 100644 --- a/devel/print-event.lua +++ b/devel/print-event.lua @@ -34,7 +34,7 @@ end local function print_event(event) local str = df.new("string") local ctx = df.history_event_context:new() - event:getSentence(str, ctx) + event:getSentence(str, ctx, true, false) ctx:delete() print(str.value) str:delete() diff --git a/docs/autotraining.rst b/docs/autotraining.rst new file mode 100644 index 0000000000..360f4d08e5 --- /dev/null +++ b/docs/autotraining.rst @@ -0,0 +1,41 @@ +autotraining +============ + +.. dfhack-tool:: + :summary: Assigns citizens to a military squad until they have fulfilled their need for Martial Training + :tags: fort auto bugfix units + +This script automatically assigns citizens with the need for military training to designated training squads. + +You need to have at least one squad that is set up for training. The squad should be set to "Constant Training" in the military screen. The squad doesn't need months off. The members leave the squad once they have satisfied their need for military training. + +The configured uniform determines the skills that are acquired by the training dwarves. Providing "No Uniform" is a perfectly valid choice and will make your militarily inclined civilians become wrestlers over time. However, you can also provide weapons and armor to pre-train civilians for future drafts. + +Once you have made squads for training use `gui/autotraining` to select the squads and ignored units, as well as the needs threshhold. + +Usage +----- + + ``autotraining []`` + +Examples +-------- + +``autotraining`` + Current status of script + +``enable autotraining`` + Checks to see if you have fullfilled the creation of a training squad. + If there is no squad marked for training use, a clickable notification will appear letting you know to set one up/ + Searches your fort for dwarves with a need for military training, and begins assigning them to a training squad. + Once they have fulfilled their need they will be removed from their squad to be replaced by the next dwarf in the list. + +``disable autotraining`` + Stops adding new units to the squad. + +Options +------- + ``-t`` + Use integer values. (Default 5000) + The negative need threshhold to trigger for each citizen + The greater the number the longer before a dwarf is added to the waiting list. diff --git a/docs/combine.rst b/docs/combine.rst index 9a0ec5f530..7dea937672 100644 --- a/docs/combine.rst +++ b/docs/combine.rst @@ -38,7 +38,9 @@ Commands ``all`` Search all stockpiles. ``here`` - Search the currently selected stockpile. + Search the currently selected stockpile, or the stockpile that the + currently-seelected item is in, or the stockpile that the currently- + displayed item-list is in. Options ------- diff --git a/docs/deathcause.rst b/docs/deathcause.rst index c9a2ae0a06..dac8a39ab9 100644 --- a/docs/deathcause.rst +++ b/docs/deathcause.rst @@ -14,3 +14,26 @@ Usage :: deathcause + +API +--- + +The ``deathcause`` script can be called programmatically by other scripts, either via the +commandline interface with ``dfhack.run_script()`` or via the API functions +defined in :source-scripts:`deathcause.lua`, available from the return value of +``reqscript('deathcause')``: + +* ``getDeathCause(unit or historical_figure)`` + +Returns a string with the unit or historical figure's cause of death. Note that using a historical +figure will sometimes provide more information than using a unit. + + + API usage example:: + + local dc = reqscript('deathcause') + + -- Note: this is an arguably bad example because this is the same as running deathcause + -- from the launcher, but this would theoretically still work. + local deathReason = dc.getDeathCauseFromUnit(dfhack.gui.getSelectedUnit()) + print(deathReason) diff --git a/docs/empty-bin.rst b/docs/empty-bin.rst index 1d45eb81aa..e6e8bf4088 100644 --- a/docs/empty-bin.rst +++ b/docs/empty-bin.rst @@ -25,18 +25,20 @@ Examples -------- ``empty-bin`` - Empty the contents of selected containers or all containers in the selected stockpile or building, except containers with liquids, onto the floor. + Empty the contents of selected containers or all containers in the selected stockpile or building, except containers with liquids or powders, onto the floor. -``empty-bin --liquids`` - Empty the contents of selected containers or all containers in the selected stockpile or building, including containers with liquids, onto the floor. +``empty-bin --force`` + Empty the contents of selected containers or all containers in the selected stockpile or building, including containers with liquids or powders, onto the floor. + +``empty-bin --recursive --force`` + Empty the contents of selected containers or all containers in the selected stockpile or building, including containers with liquids/powders and containers contents that are containers, such as a bags of seeds or filled waterskins, onto the floor. -``empty-bin --recursive --liquids`` - Empty the contents of selected containers or all containers in the selected stockpile or building, including containers with liquids and containers contents that are containers, such as a bags of seeds or filled waterskins, onto the floor. Options --------------- +------- ``-r``, ``--recursive`` - Recursively empty containers. -``-l``, ``--liquids`` - Move contained liquids (DRINK and LIQUID_MISC) to the floor, making them unusable. + Recursively empty containers. + +``-f``, ``--force`` + Move contained liquid and powders (DRINK, LIQUID_MISC and POWDER_MISC) to the floor, making them unusable. diff --git a/docs/entomb.rst b/docs/entomb.rst new file mode 100644 index 0000000000..1352b990e4 --- /dev/null +++ b/docs/entomb.rst @@ -0,0 +1,66 @@ +entomb +====== + +.. dfhack-tool:: + :summary: Entomb any corpse into tomb zones. + :tags: fort items buildings + +Assign any unit regardless of citizenship, residency, pet status, +or affiliation to an unassigned tomb zone for burial. + +Usage +----- + +``entomb []`` + +Select a unit's corpse or body part, or specify the unit's ID +when executing this script to assign an unassigned tomb zone to +the unit, and flag the unit's corpse as well as any severed body +parts to become valid items for interment. + +Optionally, specify the tomb zone's ID to assign a specific tomb +zone to the unit. + +A non-citizen, non-resident, or non-pet unit that is still alive +may even be assigned a tomb zone if they have lost any body part +that can be placed inside a tomb, e.g. teeth or severed limbs. +New corpse items after a tomb has already been assigned will not +be properly interred until the script is executed again with the +unit ID specified, or the unit's corpse or any body part selected. + +If executed on slaughtered animals, all its butchering returns will +become valid burial items and no longer usable for cooking or crafting. + +Examples +-------- + +``entomb --unit `` + Assign an unassigned tomb zone to the unit with the specified ID. + +``entomb --tomb `` + Assign a tomb zone with the specified ID to the selected corpse + item's unit. + +``entomb -u -t -h`` + Assign a tomb zone with the specified ID to the unit with the + specified ID and task all its burial items for simultaneous + hauling into the coffin in the tomb zone. + +Options +------- + +``-u``, ``--unit `` + Specify the ID of the unit to be assigned to a tomb zone. + +``-t``, ``--tomb `` + Specify the ID of the zone into which a unit will be interred. + +``-a``, ``--add-item`` + Add a selected item, or multiple items at the keyboard cursor's + position to be interred together with a unit. A unit or tomb + zone ID must be specified when calling this option. + +``-n``, ``--haul-now`` + Task all of the unit's burial items for simultaneous hauling + into the coffin of its assigned tomb zone. This option can be + called even after a tomb zone is already assigned to the unit. diff --git a/docs/fix/archery-practice.rst b/docs/fix/archery-practice.rst new file mode 100644 index 0000000000..89cf6e0d81 --- /dev/null +++ b/docs/fix/archery-practice.rst @@ -0,0 +1,75 @@ +fix/archery-practice +==================== + +.. dfhack-tool:: + :summary: Fix quivers and training ammo items to allow archery practice to take place. + :tags: fort bugfix items + +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. + +Note +---- + +The bug preventing units from initiating archery practice was fixed in +DF version 53.01. See below for more information about the issue and how +this tool works to mitigate it. Running this tool for any other archery +related issues will not yield useful results. + +Usage +----- + +``fix/archery-practice`` + 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`` + 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 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. + +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 +only one stack of ammo item assigned for training. This is sometimes +indicated on the unit by the 'Soldier (no item)' status. + +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. + +Another issue in 52.05 is that units will not practice archery if their +quiver is not the last item in their inventory. + +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 +----------- + +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 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/docs/fix/codex-pages.rst b/docs/fix/codex-pages.rst new file mode 100644 index 0000000000..b5378622bf --- /dev/null +++ b/docs/fix/codex-pages.rst @@ -0,0 +1,44 @@ +fix/codex-pages +=============== + +.. dfhack-tool:: + :summary: Add pages to written content that have no pages. + :tags: fort bugfix items + +Add pages to codices, quires, and scrolls that do not have specified page counts. + +Usage +----- + +``fix/codex-pages [this|site|all]`` + +Pages will be added to written works that do not have properly specified page +counts. The number of pages to be added will be determined mainly by the type +of the written content, modified by its writing style and the strength of the +style, with weighted randomization. + +Options +------- + +``this`` + Add pages to the selected codex, quire, or scroll item. + +``site`` + Add pages to all written works that are currently in the player's fortress. + +``all`` + Add pages to all written works to have ever existed in the world. + +Note +---- + +This tool mitigates :bug:`9268` by generating new, randomized information for +written content that do not have the start and end pages specified in their +data structure. It cannot retrieve page count from written content that was +already missing the page count information. + +Also, unbound quires and scrolls do not display the number of pages they contain +in their item description even if the data structure of their written content +holds the information. However, once a quire that has written content with +appropriately specified page count information is bound into a codex, its page +count will be properly displayed in the resulting codex's item description. diff --git a/docs/fix/symbol-unstick.rst b/docs/fix/symbol-unstick.rst new file mode 100644 index 0000000000..82b3f77db7 --- /dev/null +++ b/docs/fix/symbol-unstick.rst @@ -0,0 +1,18 @@ +fix/symbol-unstick +================== + +.. dfhack-tool:: + :summary: Unstick noble symbols that cannot be re-designated. + :tags: fort bugfix items + +Remove symbol designation from artifacts that cannot be re-designated +after the noble's promotion to a higher position. + +Usage +----- + +``fix/symbol-unstick`` + +Select an artifact that was designated as a noble's symbol and run the +command to remove its designation as a symbol. The operation will only +be performed if the symbol is claimed by a vacated noble position. diff --git a/docs/gui/adv-finder.rst b/docs/gui/adv-finder.rst new file mode 100644 index 0000000000..a1d12128e7 --- /dev/null +++ b/docs/gui/adv-finder.rst @@ -0,0 +1,112 @@ +gui/adv-finder +============== + +.. dfhack-tool:: + :summary: Find and track historical figures and artifacts + :tags: adventure armok inspection items units + +A real-time tracker for historical figures and artifacts. Select a target by +clicking the settings icon [☼] and selecting an entry from the list in the +relevant tab. The list can be filtered by search string, as well as by +excluding dead figures (displayed in red text). Artifacts can exclude books, +and the "dead" option excludes artifacts held by dead figures (which are +generally unrecoverable). Dismissing the screen (e.g., right-click) will +close the target search window first. A second dismissal will close the +finder window, but target settings will be preserved until the world is +unloaded. + +Your coordinates will be kept up to date alongside your target's. There are +two types of coordinates, and they will be displayed as long as they can be +determined. + +========== ========== +Coord Type Meaning +========== ========== +Global Distance in map blocks from the world origin (northwest corner). + The adventurer usually moves by 3 blocks during fast travel, but + slows to 1 when the zoomed site map is displayed. Equivalent to + 16 local tiles. Always available except for targets with an + indeterminate location. +Local Tile coordinates, available outside of fast travel and sleeping. + Your target's local coordinates are displayed when nearby and + loaded. Local coordinates will remain consistent within a site, but + may jump around in the wilderness as areas of the world are loaded. +========== ========== + +For global coordinates, the Z component will only be displayed if it can be +specifically determined by the location type. This represents an underground +layer depth, so the surface is indicated by ``Z0`` and the first cavern layer +is ``Z-1``. + +A compass and relative coordinates will be displayed. The relative coordinate +display uses the most precise coordinate type shared between you and your +target. + +There are six types of location types displayed for targets: + +============= ========== +Location Type Meaning +============= ========== +Nearby The target is loaded into the map area and the local + coordinates will be displayed. If you don't see this when you're + in the correct area and outside fast travel, then the target + isn't loading for some reason and you'll never be able to find + them. +Site The target is located within a site. The text displays + "At " and the global coords will represent the center + of the site if the target doesn't track its own precise + coordinates (e.g., worldgen being vague). +Traveling The target is traveling around the world map like an army. +Wilderness The target is somewhere on the surface not in a site. +Underground The target is somewhere in the caverns not in a site. +None The target's location isn't defined in the game world. + Maybe they're a deity. Maybe they got dropped off in limbo + after their army disbanded. If they're dead, the location + wasn't recorded properly in history. The text displays "Missing" + if they're dead or can die of old age, else "Transcendent" + because nothing can touch them. +============= ========== + +Dead figures generally can't be encountered at all, and they take their items +with them if they weren't separated properly by worldgen. The coord given is +usually a death or abstract burial location, but the corpse isn't guaranteed to +exist. Generally, wilderness and underground locations only have coords if you +left something there in adventure mode. Anything lost there during worldgen or a +fort mode mission likely can't be located. Anything in a site is usually a safe +bet, but sometimes items won't load. (Fort missions can be used to acquire these +for later retrieval, however.) Traveling targets are always valid. + +Usage +----- + +:: + + gui/adv-finder [] + +Examples +-------- + +``gui/adv-finder`` + Open the finder window (unless already open). Target will be blank on first + use, but maintained on future invocations. +``gui/adv-finder --histfig 1234`` + Track the historical figure with ID #1234. Finder will be opened if not + already. +``gui/adv-finder -h -1 -a -1`` + Clear any target so it's just the adventurer. Finder will be opened if not + already. +``gui/adv-finder --debug`` + Display selected target IDs in the finder window title bar. Finder will be + opened if not already. This setting isn't saved, so it can be disabled by + invoking ``gui/adv-finder`` again without the option. + +Options +------- + +``-h``, ``--histfig `` + Set the target to the historical figure with the given ID. +``-a``, ``--artifact `` + Set the target to the artifact record with the given ID. (Not an item ID!) +``-d``, ``--debug`` + Display selected target IDs in the finder window title bar. Doesn't persist + between invocations. diff --git a/docs/gui/aquifer.rst b/docs/gui/aquifer.rst index 52d47541b8..a595e197d1 100644 --- a/docs/gui/aquifer.rst +++ b/docs/gui/aquifer.rst @@ -12,7 +12,7 @@ tiles or warm tiles). Note that "just damp" tiles will still be highlighted if they are otherwise already visible. You can draw boxes around areas of tiles to alter their aquifer properties, or -you can use the :kbd:`Ctrl`:kbd:`A`` shortcut to affect entire layers at a time. +you can use the :kbd:`Ctrl`:kbd:`A` shortcut to affect entire layers at a time. If you want to see where the aquifer tiles are so you can designate digging, please run `gui/reveal`. If you only want to see the aquifer tiles and not diff --git a/docs/gui/autotraining.rst b/docs/gui/autotraining.rst new file mode 100644 index 0000000000..a86b28adf9 --- /dev/null +++ b/docs/gui/autotraining.rst @@ -0,0 +1,15 @@ +gui/autotraining +================ + +.. dfhack-tool:: + :summary: GUI interface for ``autotraining`` + :tags: fort auto interface + +This is an in-game configuration interface for `autotraining`. You can pick squads for training, select ignored units, and set the needs threshold. + +Usage +----- + +:: + + gui/autotraining diff --git a/docs/gui/design.rst b/docs/gui/design.rst index e858fc3d09..1c182ad262 100644 --- a/docs/gui/design.rst +++ b/docs/gui/design.rst @@ -45,7 +45,7 @@ Shapes - Spacing can be increased/decreased using 'T'/'t'. - They can be inverted using 'i'. - Diagonal - - Direction can be reversed using 'r'. + - Direction can be reversed using 'R'. - Spacing can be increased/decreased using 'T'/'t'. - They can be inverted using 'i'. - Line diff --git a/docs/gui/keybinds.rst b/docs/gui/keybinds.rst new file mode 100644 index 0000000000..f6031a27ce --- /dev/null +++ b/docs/gui/keybinds.rst @@ -0,0 +1,30 @@ +gui/keybinds +============ + +.. dfhack-tool:: + :summary: Manage your dfhack keybinds visually. + :tags: dfhack + +This tool allows you to create, edit, save, and delete custom keybinds that +run dfhack commands. + +Usage +----- + +:: + + gui/keybinds + +Focus Strings +------------- + +Keybinds may have a focus filter applied, enabling or disabling the keybind +based on the current open menu or gamemode. More information on the percise +format can be found in `keybinding`. + +Saved Keybinds +-------------- + +If saved, all currently active keybinds are stored in a dfhack init script at +``dfhack-config/init/dfhack.auto.keybinds.init``. The save does not remove any +keybinds set in other init scripts, nor created in-game. diff --git a/docs/gui/siegemanager.rst b/docs/gui/siegemanager.rst new file mode 100644 index 0000000000..45ae1f3d69 --- /dev/null +++ b/docs/gui/siegemanager.rst @@ -0,0 +1,16 @@ +gui/siegemanager +================ + +.. dfhack-tool:: + :summary: Manage siege engines at a glance + :tags: buildings interface productivity + +This interface provides a list of siege engines, their ammo count, and current active +jobs whilst providing shortcuts to configure their firing/standy mode and view them in-world. + +Usage +----- + +:: + + gui/siegemanager diff --git a/docs/husbandry.rst b/docs/husbandry.rst new file mode 100644 index 0000000000..27fa459cfe --- /dev/null +++ b/docs/husbandry.rst @@ -0,0 +1,61 @@ +husbandry +========= + +.. dfhack-tool:: + :summary: Automatically milk and shear animals. + :tags: fort auto + +This tool will automatically create milking and shearing orders at farmer's +workshops. Unlike the ``automilk`` and ``autoshear`` options from the control +panel, which create general work orders for milking and shearing jobs, +``husbandry`` will directly create jobs for individual animals at specific +workshops. This allows milking and shearing jobs to reliably be created at +nearby workshops (e.g. inside the pasture that an animal is assigned to), +minimizing the labor required to re-pasture animals after milking or shearing, +in particular in the case of multiple pastures that are far apart. + + +Usage +----- + +:: + + enable husbandry + husbandry [status] + husbandry now + husbandry [set|unset] [shearing|milking|roaming|pasture]+ + +Flags can be set or unset using the command ``husbandry set`` or ``husbandry +unset``. The ``shearing`` and ``milking`` flags (both enabled by default) +control whether shearing or milking jobs are created at all. + +Further, ``husbandry`` distinguishes between animals that are assigned to +pastures and those that are "roaming". + +If an animal is pastured and the pasture contains at least one workshop with the +appropriate labour (i.e. milking or shearing) enabled, jobs will be created +exclusively at those workshops. If the pasture does not contain a workshop with +the appropriate labor enabled the behavior depends on the ``pasture`` flag +(disabled by default): if set, no jobs will be created at workshops outside of +pastures, otherwise jobs may be created at the closest workshop in your fort. + +For animals that are roaming, jobs will only be created if the ``roaming`` flag +is set, which is the default. In this case, jobs are created at the closest +workshop with the appropriate labours enabled. + +Examples +-------- + +``enable husbandry`` + Start generating milking and shearing orders for animals. + +``husbandry now`` + Run a single cycle, detecting animals that can be milked/sheared an creating + jobs. Does not require the tool to be enabled. + +``husbandry unset roaming`` + Disable the creation of jobs for roaming animals. + +``husbandry set milking shearing pasture`` + Create milking and shearing jobs for pastured animals, but only at workshops + inside their pastures. diff --git a/docs/immortal-cravings.rst b/docs/immortal-cravings.rst index 9ccb90cf5d..cd8b09e6b3 100644 --- a/docs/immortal-cravings.rst +++ b/docs/immortal-cravings.rst @@ -6,10 +6,10 @@ immortal-cravings :tags: fort gameplay When enabled, this script watches your fort for units that have no physiological -need to eat or drink but still have personality needs that can only be satisfied -by eating or drinking (e.g. necromancers or goblins). This enables those units -to help themselves to a drink or a meal when they crave one and are not -otherwise occupied. +need to eat or drink but are still alcohol dependent or have personality needs +that can only be satisfied by eating or drinking (e.g. necromancers or goblins). +This enables those units to help themselves to a drink or a meal when they crave +one and are not otherwise occupied. Usage ----- diff --git a/docs/item.rst b/docs/item.rst index 465382eebe..403dadcafe 100644 --- a/docs/item.rst +++ b/docs/item.rst @@ -49,7 +49,8 @@ Examples flood-fill to create a burrow covering an entire cavern layer). ``item melt -t weapon -m steel --max-quality 3`` - Designate all steel weapons whose quality is at most superior for melting. + Designate all steel weapons whose core quality is at most superior for + melting. ``item hide -t boulder --scattered`` Hide all scattered boulders, i.e. those that are not in stockpiles. @@ -121,6 +122,11 @@ Options Only include items whose quality level is at most ``integer``. Useful values are 0 (ordinary) to 5 (masterwork). +``--total-quality`` + Only applies to ``--min-quality`` and ``--max-quality`` options. Filter items + according to their total quality (to include improvements) of instead of + their core quality. + ``--stockpiled`` Only include items that are in stockpiles. Does not include empty bins, barrels, and wheelbarrows assigned as storage and transport for stockpiles. @@ -201,8 +207,12 @@ the filter is described. see above). * ``condition_quality(tab, lower, upper, negate)`` - Selects items with quality between ``lower`` and ``upper`` (Range 0-5, see - above). + Selects items with core quality between ``lower`` and ``upper`` (Range 0-5, + see above). + +* ``condition_overall_quality(tab, lower, upper, negate)`` + Selects items with total quality between ``lower`` and ``upper`` (Range 0-5, + see above). * ``condition_stockpiled(tab, negate)`` Corresponds to ``--stockpiled``. diff --git a/docs/machine-toggle.rst b/docs/machine-toggle.rst new file mode 100644 index 0000000000..020c2bb4c7 --- /dev/null +++ b/docs/machine-toggle.rst @@ -0,0 +1,21 @@ +machine-toggle +============== + +.. dfhack-tool:: + :summary: Overlay to modify pressure plates and gear assemblies after construction. + :tags: fort armok buildings interface + +This script provides 2 overlays that are managed by the `overlay` framework. +The script does nothing when executed. +Track stops and rollers are handled by `trackstop`. + +The ``pressureplate`` overlay allows the player to change the trigger settings +of a selected pressure plate after it has been constructed. Manual value entry +of ranges for minecart and creature triggers is provided, allowing greater +precision than the game interface normally permits. Incrementing or decrementing +values always restricts them to the usual intervals. + +The ``gearassembly`` overlay allows the player to toggle the state of a selected +gear assembly without linking it to a lever first. This is useful for dwarfputing +and other applications where it may be desirable to default to the disengaged +state until triggered. diff --git a/docs/resize-armor.rst b/docs/resize-armor.rst new file mode 100644 index 0000000000..1ea1e0c094 --- /dev/null +++ b/docs/resize-armor.rst @@ -0,0 +1,26 @@ +resize-armor +============ + +.. dfhack-tool:: + :summary: Resize armor and clothing. + :tags: adventure fort armok gameplay items + +Resize any armor or clothing item to suit any creature size. + +Usage +----- + +``resize-armor [