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.
develop
Alexander Gavrilov 2014-04-21 09:24:05 +04:00
parent 4b8760815d
commit 6bef167f83
9 changed files with 286 additions and 0 deletions

@ -2157,6 +2157,9 @@ for i = 1,#order do output[i] = data[order[i]] end
way enables applying the same permutation to multiple arrays. way enables applying the same permutation to multiple arrays.
This function is used by the sort plugin.</p> This function is used by the sort plugin.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">for link,item in utils.listpairs(list)</tt></p>
<p>Iterates a df-list structure, for example <tt class="docutils literal">df.global.world.job_list</tt>.</p>
</li>
<li><p class="first"><tt class="docutils literal">utils.assign(tgt, src)</tt></p> <li><p class="first"><tt class="docutils literal">utils.assign(tgt, src)</tt></p>
<p>Does a recursive assignment of src into tgt. <p>Does a recursive assignment of src into tgt.
Uses <tt class="docutils literal">df.assign</tt> if tgt is a native object ref; otherwise Uses <tt class="docutils literal">df.assign</tt> if tgt is a native object ref; otherwise

@ -2045,6 +2045,10 @@ utils
way enables applying the same permutation to multiple arrays. way enables applying the same permutation to multiple arrays.
This function is used by the sort plugin. 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)`` * ``utils.assign(tgt, src)``
Does a recursive assignment of src into tgt. Does a recursive assignment of src into tgt.

@ -9,6 +9,7 @@ DFHack future
New scripts: New scripts:
- gui/mod-manager: allows installing/uninstalling mods into df from df/mods directory. - 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. - 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: New commands:
- move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands - move the 'grow', 'extirpate' and 'immolate' commands as 'plant' subcommands

@ -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 <tt class="docutils literal"><span class="pre">fix/cloth-stockpile</span> enable</tt> every time a save game is loaded; putting <tt class="docutils literal"><span class="pre">fix/cloth-stockpile</span> enable</tt>
in <tt class="docutils literal">dfhack.init</tt> makes it run automatically.</p> in <tt class="docutils literal">dfhack.init</tt> makes it run automatically.</p>
</li> </li>
<li><p class="first">fix/build-location</p>
<p>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.</p>
</li>
</ul> </ul>
</div> </div>
<div class="section" id="gui"> <div class="section" id="gui">

@ -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`` every time a save game is loaded; putting ``fix/cloth-stockpile enable``
in ``dfhack.init`` makes it run automatically. 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/* gui/*
===== =====

@ -147,6 +147,21 @@ function make_sort_order(data,ordering)
return index return index
end 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. Recursively assign data into a table.
--]] --]]

@ -1368,6 +1368,8 @@ bool DFHack::Units::getNoblePositions(std::vector<NoblePosition> *pvec, df::unit
std::string DFHack::Units::getProfessionName(df::unit *unit, bool ignore_noble, bool plural) std::string DFHack::Units::getProfessionName(df::unit *unit, bool ignore_noble, bool plural)
{ {
CHECK_NULL_POINTER(unit);
std::string prof = unit->custom_profession; std::string prof = unit->custom_profession;
if (!prof.empty()) if (!prof.empty())
return prof; 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) int8_t DFHack::Units::getProfessionColor(df::unit *unit, bool ignore_noble)
{ {
CHECK_NULL_POINTER(unit);
std::vector<NoblePosition> np; std::vector<NoblePosition> np;
if (!ignore_noble && getNoblePositions(&np, unit)) if (!ignore_noble && getNoblePositions(&np, unit))

@ -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()

@ -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