From 6bef167f8342176d58aafcda98c726759bb9a8c2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 21 Apr 2014 09:24:05 +0400 Subject: [PATCH] Add a couple of useful scripts and fix two missing NULL checks. - A script to unstick jobs trying to build walls from the same tile. - A devel script for viewing the path a unit is currently following. --- Lua API.html | 3 + Lua API.rst | 4 + NEWS | 1 + Readme.html | 5 + Readme.rst | 7 ++ library/lua/utils.lua | 15 +++ library/modules/Units.cpp | 4 + scripts/devel/unit-path.lua | 210 +++++++++++++++++++++++++++++++++ scripts/fix/build-location.lua | 37 ++++++ 9 files changed, 286 insertions(+) create mode 100644 scripts/devel/unit-path.lua create mode 100644 scripts/fix/build-location.lua diff --git a/Lua API.html b/Lua API.html index eec1a0ea2..bd17cc644 100644 --- a/Lua API.html +++ b/Lua API.html @@ -2157,6 +2157,9 @@ for i = 1,#order do output[i] = data[order[i]] end way enables applying the same permutation to multiple arrays. This function is used by the sort plugin.

+
  • for link,item in utils.listpairs(list)

    +

    Iterates a df-list structure, for example df.global.world.job_list.

    +
  • utils.assign(tgt, src)

    Does a recursive assignment of src into tgt. Uses df.assign if tgt is a native object ref; otherwise diff --git a/Lua API.rst b/Lua API.rst index 37ff003ed..072d9265f 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -2045,6 +2045,10 @@ utils way enables applying the same permutation to multiple arrays. This function is used by the sort plugin. +* ``for link,item in utils.listpairs(list)`` + + Iterates a df-list structure, for example ``df.global.world.job_list``. + * ``utils.assign(tgt, src)`` Does a recursive assignment of src into tgt. diff --git a/NEWS b/NEWS index 6e66a6017..165938e2b 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ DFHack future New scripts: - gui/mod-manager: allows installing/uninstalling mods into df from df/mods directory. - gui/clone-uniform: duplicates the currently selected uniform in the military screen. + - fix/build-location: partial work-around for bug 5991 (trying to build wall while standing on it) New commands: - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands diff --git a/Readme.html b/Readme.html index 98139cb50..b93dc3bfe 100644 --- a/Readme.html +++ b/Readme.html @@ -2930,6 +2930,11 @@ in memory and patching up some invalid reference fields. Needs to be run every time a save game is loaded; putting fix/cloth-stockpile enable in dfhack.init makes it run automatically.

  • +
  • fix/build-location

    +

    Fixes construction jobs that are stuck trying to build a wall while standing +on the same exact tile (bug 5991), designates the tile restricted traffic to +hopefully avoid jamming it again, and unsuspends them.

    +
  • diff --git a/Readme.rst b/Readme.rst index 84c4f7c9d..9b8fd07cb 100644 --- a/Readme.rst +++ b/Readme.rst @@ -2049,6 +2049,13 @@ Scripts in this subdirectory fix various bugs and issues, some of them obscure. every time a save game is loaded; putting ``fix/cloth-stockpile enable`` in ``dfhack.init`` makes it run automatically. +* fix/build-location + + Fixes construction jobs that are stuck trying to build a wall while standing + on the same exact tile (bug 5991), designates the tile restricted traffic to + hopefully avoid jamming it again, and unsuspends them. + + gui/* ===== diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 4a7a8d510..caa682c75 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -147,6 +147,21 @@ function make_sort_order(data,ordering) return index end +--[[ + Iterate a 'list' structure, e.g. df.global.world.job_list +--]] +local function next_df_list(s,link) + link = link.next + if link then + return link, link.item + end +end + +function listpairs(list) + return next_df_list, nil, list +end + + --[[ Recursively assign data into a table. --]] diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 13e23fb39..20c536cd1 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1368,6 +1368,8 @@ bool DFHack::Units::getNoblePositions(std::vector *pvec, df::unit std::string DFHack::Units::getProfessionName(df::unit *unit, bool ignore_noble, bool plural) { + CHECK_NULL_POINTER(unit); + std::string prof = unit->custom_profession; if (!prof.empty()) return prof; @@ -1514,6 +1516,8 @@ std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::pro int8_t DFHack::Units::getProfessionColor(df::unit *unit, bool ignore_noble) { + CHECK_NULL_POINTER(unit); + std::vector np; if (!ignore_noble && getNoblePositions(&np, unit)) diff --git a/scripts/devel/unit-path.lua b/scripts/devel/unit-path.lua new file mode 100644 index 000000000..dc1066bb2 --- /dev/null +++ b/scripts/devel/unit-path.lua @@ -0,0 +1,210 @@ +-- Show the internal path a unit is currently following. + +local utils = require 'utils' +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local dlg = require 'gui.dialogs' + +local tile_attrs = df.tiletype.attrs + +UnitPathUI = defclass(UnitPathUI, guidm.MenuOverlay) + +UnitPathUI.focus_path = 'unit-path' + +UnitPathUI.ATTRS { + unit = DEFAULT_NIL, +} + +function UnitPathUI:init() + self.saved_mode = df.global.ui.main.mode + +end + +function UnitPathUI:onShow() + -- with cursor, but without those ugly lines from native hauling mode + df.global.ui.main.mode = df.ui_sidebar_mode.Stockpiles +end + +function UnitPathUI:onDestroy() + self:moveCursorTo(copyall(self.unit.pos)) + df.global.ui.main.mode = self.saved_mode +end + +local function getTileType(cursor) + local block = dfhack.maps.getTileBlock(cursor) + if block then + return block.tiletype[cursor.x%16][cursor.y%16] + else + return 0 + end +end + +local function getTileWalkable(cursor) + local block = dfhack.maps.getTileBlock(cursor) + if block then + return block.walkable[cursor.x%16][cursor.y%16] + else + return 0 + end +end + +local function paintMapTile(dc, vp, cursor, pos, ...) + if not same_xyz(cursor, pos) then + local stile = vp:tileToScreen(pos) + if stile.z == 0 then + dc:seek(stile.x,stile.y):char(...) + end + end +end + +local function get_path_point(gpath,i) + return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i]) +end + +function UnitPathUI:renderPath(dc,vp,cursor) + local gpath = self.unit.path.path + local startp = self.unit.pos + local endp = self.unit.path.dest + local visible = gui.blink_visible(500) + + if visible then + paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN) + end + + local ok = nil + local pcnt = #gpath.x + if pcnt > 0 then + ok = true + + for i = 0,pcnt-1 do + local pt = get_path_point(gpath, i) + if i == 0 and not same_xyz(startp,pt) then + ok = false + end + if i == pcnt-1 and not same_xyz(endp,pt) then + ok = false + end + --[[local tile = getTileType(pt) + if not isTrackTile(tile) then + ok = false + end]] + if visible then + local char = '+' + if i < pcnt-1 then + local npt = get_path_point(gpath, i+1) + if npt.z == pt.z+1 then + char = 30 + elseif npt.z == pt.z-1 then + char = 31 + elseif npt.x == pt.x+1 then + char = 26 + elseif npt.x == pt.x-1 then + char = 27 + elseif npt.y == pt.y+1 then + char = 25 + elseif npt.y == pt.y-1 then + char = 24 + end + end + local color = COLOR_LIGHTGREEN + if getTileWalkable(pt) == 0 then color = COLOR_LIGHTRED end + paintMapTile(dc, vp, cursor, pt, char, color) + end + end + end + + if gui.blink_visible(120) then + paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN) + end + + return ok +end + +function UnitPathUI:onRenderBody(dc) + dc:clear():seek(1,1):pen(COLOR_WHITE):string("Unit Path") + + local prof = dfhack.units.getProfessionName(self.unit) + local name = dfhack.units.getVisibleName(self.unit) + + dc:seek(2,3):pen(COLOR_BLUE):string(prof) + if name and name.has_name then + dc:seek(2,4):pen(COLOR_BLUE):string(dfhack.TranslateName(name)) + end + + local cursor = guidm.getCursorPos() + local has_path = #self.unit.path.path.x>0 + + local vp = self:getViewport() + local mdc = gui.Painter.new(self.df_layout.map) + + if not has_path then + if gui.blink_visible(120) then + paintMapTile(mdc, vp, cursor, self.unit.pos, 15, COLOR_LIGHTRED, COLOR_RED) + end + + dc:seek(1,6):pen(COLOR_RED):string('Not following path') + else + self:renderPath(mdc,vp,cursor) + + dc:seek(1,6):pen(COLOR_GREEN):string(df.unit_path_goal[self.unit.path.goal]) + end + + dc:newline():pen(COLOR_GREY) + dc:newline(2):string('Speed: '..dfhack.units.computeMovementSpeed(self.unit)) + dc:newline(2):string('Slowdown: '..dfhack.units.computeSlowdownFactor(self.unit)) + + dc:newline():newline(1) + + local has_station = self.unit.idle_area_type >= 0 + + if has_station then + if gui.blink_visible(250) then + paintMapTile(mdc, vp, cursor, self.unit.idle_area, 21, COLOR_LIGHTCYAN) + end + + dc:pen(COLOR_GREEN):string(df.unit_station_type[self.unit.idle_area_type]) + dc:newline():newline(2):pen(COLOR_GREY):string('Threshold: '..self.unit.idle_area_threshold) + else + dc:pen(COLOR_RED):string('No station'):newline():newline(2) + end + + dc:newline():newline(1):string('At cursor:') + dc:newline():newline(2) + + local tile = getTileType(cursor) + dc:string(df.tiletype[tile],COLOR_CYAN) + + dc:newline():newline(1):pen(COLOR_WHITE) + dc:key('CUSTOM_Z'):string(": Zoom unit, ") + dc:key('CUSTOM_G'):string(": Zoom goal",COLOR_GREY,nil,has_path) + dc:newline(1) + dc:key('CUSTOM_N'):string(": Zoom station",COLOR_GREY,nil,has_station) + dc:newline():newline(1) + dc:key('LEAVESCREEN'):string(": Back") +end + +function UnitPathUI:onInput(keys) + if keys.CUSTOM_Z then + self:moveCursorTo(copyall(self.unit.pos)) + elseif keys.CUSTOM_G then + if #self.unit.path.path.x > 0 then + self:moveCursorTo(copyall(self.unit.path.dest)) + end + elseif keys.CUSTOM_N then + if self.unit.idle_area_type > 0 then + self:moveCursorTo(copyall(self.unit.idle_area)) + end + elseif keys.LEAVESCREEN then + self:dismiss() + elseif self:propagateMoveKeys(keys) then + return + end +end + +local unit = dfhack.gui.getSelectedUnit(true) + +if not unit or not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/ViewUnits/Some/') then + qerror("This script requires the main dwarfmode view in 'v' mode with a unit selected") +end + +UnitPathUI{ unit = unit }:show() diff --git a/scripts/fix/build-location.lua b/scripts/fix/build-location.lua new file mode 100644 index 000000000..0eea2c780 --- /dev/null +++ b/scripts/fix/build-location.lua @@ -0,0 +1,37 @@ +-- Lets constructions reconsider the build location. + +-- Partial work-around for http://www.bay12games.com/dwarves/mantisbt/view.php?id=5991 + +local utils = require('utils') + +local count = 0 + +for link,job in utils.listpairs(df.global.world.job_list) do + local job = link.item + local place = dfhack.job.getHolder(job) + + if job.job_type == df.job_type.ConstructBuilding + and place and place:isImpassableAtCreation() + and job.item_category[0] + then + local cpos = utils.getBuildingCenter(place) + + if same_xyz(cpos, job.pos) then + -- Reset the flag + job.item_category[0] = false + job.flags.suspend = false + + -- Mark the tile restricted traffic + local dsgn,occ = dfhack.maps.getTileFlags(cpos) + dsgn.traffic = df.tile_traffic.Restricted + + count = count + 1 + end + end +end + +print('Found and unstuck '..count..' construct building jobs.') + +if count > 0 then + df.global.process_jobs = true +end