diff --git a/LUA_API.rst b/LUA_API.rst
index e20b946d8..d8d53a4d1 100644
--- a/LUA_API.rst
+++ b/LUA_API.rst
@@ -666,6 +666,14 @@ Job module
Returns the unit performing the job.
+* ``dfhack.job.checkBuildingsNow()``
+
+ Instructs the game to check buildings for jobs next frame and assign workers.
+
+* ``dfhack.job.checkDesignationsNow()``
+
+ Instructs the game to check designations for jobs next frame and assign workers.
+
* ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures.
@@ -803,6 +811,10 @@ Maps module
Returns the biome info struct for the given global map region.
+* ``dfhack.maps.enableBlockUpdates(block[,flow,temperature])``
+
+ Enables updates for liquid flow or temperature, unless already active.
+
* ``dfhack.maps.getGlobalInitFeature(index)``
Returns the global feature object with the given index.
@@ -1054,7 +1066,7 @@ Events:
Emitted when a burrow might have been renamed either through
the game UI, or ``renameBurrow()``.
-* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)``
+* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)``
Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled.
diff --git a/Lua API.html b/Lua API.html
index 7aa8e651a..846299a35 100644
--- a/Lua API.html
+++ b/Lua API.html
@@ -912,6 +912,12 @@ The is_bright boolean actually seems to invert the brightness.
dfhack.job.getWorker(job)
Returns the unit performing the job.
+dfhack.job.checkBuildingsNow()
+Instructs the game to check buildings for jobs next frame and assign workers.
+
+dfhack.job.checkDesignationsNow()
+Instructs the game to check designations for jobs next frame and assign workers.
+
dfhack.job.is_equal(job1,job2)
Compares important fields in the job and nested item structures.
@@ -1023,6 +1029,9 @@ Returns false in case of error.
dfhack.maps.getRegionBiome(region_coord2d)
Returns the biome info struct for the given global map region.
+dfhack.maps.enableBlockUpdates(block[,flow,temperature])
+Enables updates for liquid flow or temperature, unless already active.
+
dfhack.maps.getGlobalInitFeature(index)
Returns the global feature object with the given index.
@@ -1236,7 +1245,7 @@ module file is still necessary for require to
Emitted when a burrow might have been renamed either through
the game UI, or renameBurrow().
-onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)
+onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)
Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled.
diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp
index d554754e4..1baa4045d 100644
--- a/library/LuaApi.cpp
+++ b/library/LuaApi.cpp
@@ -637,6 +637,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,printJobDetails),
WRAPM(Job,getHolder),
WRAPM(Job,getWorker),
+ WRAPM(Job,checkBuildingsNow),
+ WRAPM(Job,checkDesignationsNow),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }
@@ -753,6 +755,7 @@ static const luaL_Reg dfhack_items_funcs[] = {
static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, getRegionBiome),
+ WRAPM(Maps, enableBlockUpdates),
WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween),
diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h
index 8b917ad70..aff04128c 100644
--- a/library/include/DataFuncs.h
+++ b/library/include/DataFuncs.h
@@ -143,6 +143,23 @@ INSTANTIATE_WRAPPERS(4, (OSTREAM_ARG,A1,A2,A3,A4), (out,vA1,vA2,vA3,vA4),
INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5))
INSTANTIATE_WRAPPERS(5, (A1,A2,A3,A4,A5), (vA1,vA2,vA3,vA4,vA5),
LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4); LOAD_ARG(A5);)
+INSTANTIATE_WRAPPERS(5, (OSTREAM_ARG,A1,A2,A3,A4,A5), (out,vA1,vA2,vA3,vA4,vA5),
+ LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2);
+ LOAD_ARG(A3); LOAD_ARG(A4); LOAD_ARG(A5);)
+#undef FW_TARGS
+
+#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6
+INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6))
+INSTANTIATE_WRAPPERS(6, (A1,A2,A3,A4,A5,A6), (vA1,vA2,vA3,vA4,vA5,vA6),
+ LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);)
+INSTANTIATE_WRAPPERS(6, (OSTREAM_ARG,A1,A2,A3,A4,A5,A6), (out,vA1,vA2,vA3,vA4,vA5,vA6),
+ LOAD_OSTREAM(out); LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);
+ LOAD_ARG(A4); LOAD_ARG(A5); LOAD_ARG(A6);)
+#undef FW_TARGS
+
+#define FW_TARGS class A1, class A2, class A3, class A4, class A5, class A6, class A7
+INSTANTIATE_RETURN_TYPE((A1,A2,A3,A4,A5,A6,A7))
#undef FW_TARGS
#undef FW_TARGSC
diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h
index c3a7921c3..a52db2572 100644
--- a/library/include/LuaTools.h
+++ b/library/include/LuaTools.h
@@ -424,3 +424,17 @@ namespace DFHack {namespace Lua {
name##_event.invoke(out, 4); \
} \
}
+
+#define DEFINE_LUA_EVENT_5(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5) \
+ static DFHack::Lua::Notification name##_event(df::wrap_function(handler, true)); \
+ void name(color_ostream &out, arg_type1 arg1, arg_type2 arg2, arg_type3 arg3, arg_type4 arg4, arg_type5 arg5) { \
+ handler(out, arg1, arg2, arg3, arg4, arg5); \
+ if (auto state = name##_event.get_state()) { \
+ DFHack::Lua::Push(state, arg1); \
+ DFHack::Lua::Push(state, arg2); \
+ DFHack::Lua::Push(state, arg3); \
+ DFHack::Lua::Push(state, arg4); \
+ DFHack::Lua::Push(state, arg5); \
+ name##_event.invoke(out, 5); \
+ } \
+ }
diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h
index a4e173a8d..9ce72c326 100644
--- a/library/include/modules/Job.h
+++ b/library/include/modules/Job.h
@@ -57,6 +57,10 @@ namespace DFHack
DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(df::job *job);
+ // Instruct the game to check and assign workers
+ DFHACK_EXPORT void checkBuildingsNow();
+ DFHACK_EXPORT void checkDesignationsNow();
+
DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
// lists jobs with ids >= *id_var, and sets *id_var = *job_next_id;
diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h
index 0b4e78b21..109a20a41 100644
--- a/library/include/modules/MapCache.h
+++ b/library/include/modules/MapCache.h
@@ -93,6 +93,12 @@ public:
Block(MapCache *parent, DFCoord _bcoord);
~Block();
+ DFCoord getCoord() { return bcoord; }
+
+ void enableBlockUpdates(bool flow = false, bool temp = false) {
+ Maps::enableBlockUpdates(block, flow, temp);
+ }
+
/*
* All coordinates are taken mod 16.
*/
@@ -208,11 +214,8 @@ public:
dirty_designations = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
index_tile(designation,p) = des;
- if(des.bits.dig)
- {
- dirty_blockflags = true;
- blockflags.bits.designated = true;
- }
+ if(des.bits.dig && block)
+ block->flags.bits.designated = true;
return true;
}
@@ -236,15 +239,7 @@ public:
t_blockflags BlockFlags()
{
- return blockflags;
- }
- bool setBlockFlags(t_blockflags des)
- {
- if(!valid) return false;
- dirty_blockflags = true;
- //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
- blockflags = des;
- return true;
+ return block ? block->flags : t_blockflags();
}
bool Write();
@@ -273,7 +268,6 @@ private:
bool dirty_designations:1;
bool dirty_tiles:1;
bool dirty_temperatures:1;
- bool dirty_blockflags:1;
bool dirty_occupancies:1;
DFCoord bcoord;
@@ -328,7 +322,6 @@ private:
designations40d designation;
occupancies40d occupancy;
- t_blockflags blockflags;
t_temperatures temp1;
t_temperatures temp2;
diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h
index fc18ca7e1..c6dc7c404 100644
--- a/library/include/modules/Maps.h
+++ b/library/include/modules/Maps.h
@@ -257,6 +257,9 @@ inline df::tile_occupancy *getTileOccupancy(df::coord pos) {
DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos);
+// Enables per-frame updates for liquid flow and/or temperature.
+DFHACK_EXPORT void enableBlockUpdates(df::map_block *blk, bool flow = false, bool temperature = false);
+
/// sorts the block event vector into multiple vectors by type
/// mineral veins, what's under ice, blood smears and mud
extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block,
diff --git a/library/lua/dumper.lua b/library/lua/dumper.lua
new file mode 100644
index 000000000..44e787db2
--- /dev/null
+++ b/library/lua/dumper.lua
@@ -0,0 +1,234 @@
+--[[ DataDumper.lua
+Copyright (c) 2007 Olivetti-Engineering SA
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+]]
+
+local _ENV = mkmodule('dumper')
+
+local dumplua_closure = [[
+local closures = {}
+local function closure(t)
+ closures[#closures+1] = t
+ t[1] = assert(loadstring(t[1]))
+ return t[1]
+end
+
+for _,t in pairs(closures) do
+ for i = 2,#t do
+ debug.setupvalue(t[1], i-1, t[i])
+ end
+end
+]]
+
+local lua_reserved_keywords = {
+ 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for',
+ 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
+ 'return', 'then', 'true', 'until', 'while' }
+
+local function keys(t)
+ local res = {}
+ local oktypes = { stringstring = true, numbernumber = true }
+ local function cmpfct(a,b)
+ if oktypes[type(a)..type(b)] then
+ return a < b
+ else
+ return type(a) < type(b)
+ end
+ end
+ for k in pairs(t) do
+ res[#res+1] = k
+ end
+ table.sort(res, cmpfct)
+ return res
+end
+
+local c_functions = {}
+for _,lib in pairs{'_G', 'string', 'table', 'math',
+ 'io', 'os', 'coroutine', 'package', 'debug'} do
+ local t = _G[lib] or {}
+ lib = lib .. "."
+ if lib == "_G." then lib = "" end
+ for k,v in pairs(t) do
+ if type(v) == 'function' and not pcall(string.dump, v) then
+ c_functions[v] = lib..k
+ end
+ end
+end
+
+function DataDumper(value, varname, fastmode, ident, indent_step)
+ indent_step = indent_step or 2
+ local defined, dumplua = {}
+ -- Local variables for speed optimization
+ local string_format, type, string_dump, string_rep =
+ string.format, type, string.dump, string.rep
+ local tostring, pairs, table_concat =
+ tostring, pairs, table.concat
+ local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0
+ setmetatable(strvalcache, {__index = function(t,value)
+ local res = string_format('%q', value)
+ t[value] = res
+ return res
+ end})
+ local fcts = {
+ string = function(value) return strvalcache[value] end,
+ number = function(value) return value end,
+ boolean = function(value) return tostring(value) end,
+ ['nil'] = function(value) return 'nil' end,
+ ['function'] = function(value)
+ return string_format("loadstring(%q)", string_dump(value))
+ end,
+ userdata = function() error("Cannot dump userdata") end,
+ thread = function() error("Cannot dump threads") end,
+ }
+ local function test_defined(value, path)
+ if defined[value] then
+ if path:match("^getmetatable.*%)$") then
+ out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value])
+ else
+ out[#out+1] = path .. " = " .. defined[value] .. "\n"
+ end
+ return true
+ end
+ defined[value] = path
+ end
+ local function make_key(t, key)
+ local s
+ if type(key) == 'string' and key:match('^[_%a][_%w]*$') then
+ s = key .. "="
+ else
+ s = "[" .. dumplua(key, 0) .. "]="
+ end
+ t[key] = s
+ return s
+ end
+ for _,k in ipairs(lua_reserved_keywords) do
+ keycache[k] = '["'..k..'"] = '
+ end
+ if fastmode then
+ fcts.table = function (value)
+ -- Table value
+ local numidx = 1
+ out[#out+1] = "{"
+ for key,val in pairs(value) do
+ if key == numidx then
+ numidx = numidx + 1
+ else
+ out[#out+1] = keycache[key]
+ end
+ local str = dumplua(val)
+ out[#out+1] = str..","
+ end
+ if string.sub(out[#out], -1) == "," then
+ out[#out] = string.sub(out[#out], 1, -2);
+ end
+ out[#out+1] = "}"
+ return ""
+ end
+ else
+ fcts.table = function (value, ident, path)
+ if test_defined(value, path) then return "nil" end
+ -- Table value
+ local sep, str, numidx, totallen = " ", {}, 1, 0
+ local meta, metastr = (debug or getfenv()).getmetatable(value)
+ if meta then
+ ident = ident + 1
+ metastr = dumplua(meta, ident, "getmetatable("..path..")")
+ totallen = totallen + #metastr + 16
+ end
+ for _,key in pairs(keys(value)) do
+ local val = value[key]
+ local s = ""
+ local subpath = path
+ if key == numidx then
+ subpath = subpath .. "[" .. numidx .. "]"
+ numidx = numidx + 1
+ else
+ s = keycache[key]
+ if not s:match "^%[" then subpath = subpath .. "." end
+ subpath = subpath .. s:gsub("%s*=%s*$","")
+ end
+ s = s .. dumplua(val, ident+1, subpath)
+ str[#str+1] = s
+ totallen = totallen + #s + 2
+ end
+ if totallen > 80 then
+ sep = "\n" .. string_rep(' ', indent_step*(ident+1))
+ end
+ str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-1-indent_step).."}"
+ if meta then
+ sep = sep:sub(1,-3)
+ return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")"
+ end
+ return str
+ end
+ fcts['function'] = function (value, ident, path)
+ if test_defined(value, path) then return "nil" end
+ if c_functions[value] then
+ return c_functions[value]
+ elseif debug == nil or debug.getupvalue(value, 1) == nil then
+ return string_format("loadstring(%q)", string_dump(value))
+ end
+ closure_cnt = closure_cnt + 1
+ local res = {string.dump(value)}
+ for i = 1,math.huge do
+ local name, v = debug.getupvalue(value,i)
+ if name == nil then break end
+ res[i+1] = v
+ end
+ return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]")
+ end
+ end
+ function dumplua(value, ident, path)
+ return fcts[type(value)](value, ident, path)
+ end
+ if varname == nil then
+ varname = "return "
+ elseif varname:match("^[%a_][%w_]*$") then
+ varname = varname .. " = "
+ end
+ if fastmode then
+ setmetatable(keycache, {__index = make_key })
+ out[1] = varname
+ table.insert(out,dumplua(value, 0))
+ return table.concat(out)
+ else
+ setmetatable(keycache, {__index = make_key })
+ local items = {}
+ for i=1,10 do items[i] = '' end
+ items[3] = dumplua(value, ident or 0, "t")
+ if closure_cnt > 0 then
+ items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)")
+ out[#out+1] = ""
+ end
+ if #out > 0 then
+ items[2], items[4] = "local t = ", "\n"
+ items[5] = table.concat(out)
+ items[7] = varname .. "t"
+ else
+ items[2] = varname
+ end
+ return table.concat(items)
+ end
+end
+
+return _ENV
diff --git a/library/lua/utils.lua b/library/lua/utils.lua
index 3eeb0cc6f..96ab2b190 100644
--- a/library/lua/utils.lua
+++ b/library/lua/utils.lua
@@ -1,5 +1,7 @@
local _ENV = mkmodule('utils')
+local df = df
+
-- Comparator function
function compare(a,b)
if a < b then
@@ -26,6 +28,43 @@ function compare_name(a,b)
end
end
+-- Make a field comparator
+function compare_field(field,cmp)
+ cmp = cmp or compare
+ if field then
+ return function (a,b)
+ return cmp(a[field],b[field])
+ end
+ else
+ return cmp
+ end
+end
+
+-- Make a comparator of field vs key
+function compare_field_key(field,cmp)
+ cmp = cmp or compare
+ if field then
+ return function (a,b)
+ return cmp(a[field],b)
+ end
+ else
+ return cmp
+ end
+end
+
+function is_container(obj)
+ return df.isvalid(obj) == 'ref' and obj._kind == 'container'
+end
+
+-- Make a sequence of numbers in 1..size
+function make_index_sequence(size)
+ local index = {}
+ for i=1,size do
+ index[i] = i
+ end
+ return index
+end
+
--[[
Sort items in data according to ordering.
@@ -75,10 +114,7 @@ function make_sort_order(data,ordering)
end
-- Make an order table
- local index = {}
- for i=1,size do
- index[i] = i
- end
+ local index = make_index_sequence(size)
-- Sort the ordering table
table.sort(index, function(ia,ib)
@@ -119,7 +155,7 @@ function assign(tgt,src)
df.assign(tgt, src)
elseif type(tgt) == 'table' then
for k,v in pairs(src) do
- if type(v) == 'table' or df.isvalid(v) == 'ref' then
+ if type(v) == 'table' then
local cv = tgt[k]
if cv == nil then
cv = {}
@@ -136,4 +172,131 @@ function assign(tgt,src)
return tgt
end
+local function copy_field(obj,k,v,deep)
+ if v == nil then
+ return NULL
+ end
+ if deep then
+ local field = obj:_field(k)
+ if field == v then
+ return clone(v,deep)
+ end
+ end
+ return v
+end
+
+-- Copy the object as lua data structures.
+function clone(obj,deep)
+ if type(obj) == 'table' then
+ if deep then
+ return assign({},obj)
+ else
+ return copyall(obj)
+ end
+ elseif df.isvalid(obj) == 'ref' then
+ local kind = obj._kind
+ if kind == 'primitive' then
+ return obj.value
+ elseif kind == 'bitfield' then
+ local rv = {}
+ for k,v in pairs(obj) do
+ rv[k] = v
+ end
+ return rv
+ elseif kind == 'container' then
+ local rv = {}
+ for k,v in ipairs(obj) do
+ rv[k+1] = copy_field(obj,k,v,deep)
+ end
+ return rv
+ else -- struct
+ local rv = {}
+ for k,v in pairs(obj) do
+ rv[k] = copy_field(obj,k,v,deep)
+ end
+ return rv
+ end
+ else
+ return obj
+ end
+end
+
+-- Sort a vector or lua table
+function sort_vector(vector,field,cmp)
+ local fcmp = compare_field(field,cmp)
+ local scmp = function(a,b)
+ return fcmp(a,b) < 0
+ end
+ if df.isvalid(vector) then
+ if vector._kind ~= 'container' then
+ error('Container expected: '..tostring(vector))
+ end
+ local items = clone(vector, true)
+ table.sort(items, scmp)
+ vector:assign(items)
+ else
+ table.sort(vector, scmp)
+ end
+ return vector
+end
+
+-- Binary search in a vector or lua table
+function binsearch(vector,key,field,cmp,min,max)
+ if not(min and max) then
+ if df.isvalid(vector) then
+ min = -1
+ max = #vector
+ else
+ min = 0
+ max = #vector+1
+ end
+ end
+ local mf = math.floor
+ local fcmp = compare_field_key(field,cmp)
+ while true do
+ local mid = mf((min+max)/2)
+ if mid <= min then
+ return nil, false, max
+ end
+ local item = vector[mid]
+ local cv = fcmp(item, key)
+ if cv == 0 then
+ return item, true, mid
+ elseif cv < 0 then
+ min = mid
+ else
+ max = mid
+ end
+ end
+end
+
+-- Binary search and insert
+function insert_sorted(vector,item,field,cmp)
+ local key = item
+ if field and item then
+ key = item[field]
+ end
+ local cur,found,pos = binsearch(vector,key,field,cmp)
+ if found then
+ return false,cur,pos
+ else
+ if df.isvalid(vector) then
+ vector:insert(pos, item)
+ else
+ table.insert(vector, pos, item)
+ end
+ return true,vector[pos],pos
+ end
+end
+
+-- Binary search, then insert or overwrite
+function insert_or_update(vector,item,field,cmp)
+ local added,cur,pos = insert_sorted(vector,item,field,cmp)
+ if not added then
+ vector[pos] = item
+ cur = vector[pos]
+ end
+ return added,cur,pos
+end
+
return _ENV
\ No newline at end of file
diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp
index 557eb1881..65cf009f3 100644
--- a/library/modules/Buildings.cpp
+++ b/library/modules/Buildings.cpp
@@ -709,8 +709,7 @@ static void linkBuilding(df::building *bld)
linkRooms(bld);
- if (process_jobs)
- *process_jobs = true;
+ Job::checkBuildingsNow();
}
static void createDesign(df::building *bld, bool rough)
@@ -900,8 +899,6 @@ bool Buildings::deconstruct(df::building *bld)
{
using df::global::ui;
using df::global::world;
- using df::global::process_jobs;
- using df::global::process_dig;
using df::global::ui_look_list;
CHECK_NULL_POINTER(bld);
@@ -952,8 +949,8 @@ bool Buildings::deconstruct(df::building *bld)
}
}
- if (process_dig) *process_dig = true;
- if (process_jobs) *process_jobs = true;
+ Job::checkBuildingsNow();
+ Job::checkDesignationsNow();
return true;
}
diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp
index 149707c6e..1207c97b3 100644
--- a/library/modules/Job.cpp
+++ b/library/modules/Job.cpp
@@ -256,6 +256,18 @@ df::unit *DFHack::Job::getWorker(df::job *job)
return NULL;
}
+void DFHack::Job::checkBuildingsNow()
+{
+ if (df::global::process_jobs)
+ *df::global::process_jobs = true;
+}
+
+void DFHack::Job::checkDesignationsNow()
+{
+ if (df::global::process_dig)
+ *df::global::process_dig = true;
+}
+
bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
{
using df::global::world;
diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp
index 69f591a0f..a8b7945d1 100644
--- a/library/modules/Maps.cpp
+++ b/library/modules/Maps.cpp
@@ -54,6 +54,7 @@ using namespace std;
#include "df/world_region_details.h"
#include "df/builtin_mats.h"
#include "df/block_square_event_grassst.h"
+#include "df/z_level_flags.h"
using namespace DFHack;
using namespace df::enums;
@@ -176,6 +177,30 @@ df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos)
return &data->region_map[rgn_pos.x][rgn_pos.y];
}
+void Maps::enableBlockUpdates(df::map_block *blk, bool flow, bool temperature)
+{
+ if (!blk || !(flow || temperature)) return;
+
+ if (temperature)
+ blk->flags.bits.update_temperature = true;
+
+ if (flow)
+ {
+ blk->flags.bits.update_liquid = true;
+ blk->flags.bits.update_liquid_twice = true;
+ }
+
+ auto z_flags = world->map.z_level_flags;
+ int z_level = blk->map_pos.z;
+
+ if (z_flags && z_level >= 0 && z_level < world->map.z_count_block)
+ {
+ z_flags += z_level;
+ z_flags->bits.update = true;
+ z_flags->bits.update_twice = true;
+ }
+}
+
df::feature_init *Maps::getGlobalInitFeature(int32_t index)
{
auto data = world->world_data;
@@ -426,7 +451,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
dirty_designations = false;
dirty_tiles = false;
dirty_temperatures = false;
- dirty_blockflags = false;
dirty_occupancies = false;
valid = false;
bcoord = _bcoord;
@@ -440,7 +464,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
{
COPY(designation, block->designation);
COPY(occupancy, block->occupancy);
- blockflags = block->flags;
COPY(temp1, block->temperature_1);
COPY(temp2, block->temperature_2);
@@ -449,7 +472,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
}
else
{
- blockflags.whole = 0;
memset(designation,0,sizeof(designation));
memset(occupancy,0,sizeof(occupancy));
memset(temp1,0,sizeof(temp1));
@@ -634,11 +656,6 @@ bool MapExtras::Block::Write ()
{
if(!valid) return false;
- if(dirty_blockflags)
- {
- block->flags = blockflags;
- dirty_blockflags = false;
- }
if(dirty_designations)
{
COPY(block->designation, designation);
diff --git a/library/xml b/library/xml
index 970bb0ae9..b20eba417 160000
--- a/library/xml
+++ b/library/xml
@@ -1 +1 @@
-Subproject commit 970bb0ae9530e9c0522cb0ca8fe357815d72fab1
+Subproject commit b20eba4170a2b7f52e465849e8853c3ecc978a8e
diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp
index a6df3524b..9ff902c3d 100644
--- a/plugins/burrows.cpp
+++ b/plugins/burrows.cpp
@@ -153,10 +153,10 @@ static int next_job_id_save = 0;
static std::map diggers;
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos,
- df::tiletype old_tile, df::tiletype new_tile);
+ df::tiletype old_tile, df::tiletype new_tile, df::unit *worker);
-DEFINE_LUA_EVENT_4(onDigComplete, handle_dig_complete,
- df::job_type, df::coord, df::tiletype, df::tiletype);
+DEFINE_LUA_EVENT_5(onDigComplete, handle_dig_complete,
+ df::job_type, df::coord, df::tiletype, df::tiletype, df::unit*);
static void detect_digging(color_ostream &out)
{
@@ -179,10 +179,7 @@ static void detect_digging(color_ostream &out)
if (new_tile != it->second.old_tile)
{
- onDigComplete(out, it->second.job, pos, it->second.old_tile, new_tile);
-
- //if (worker && !worker->job.current_job)
- // worker->counters.think_counter = worker->counters.job_counter = 0;
+ onDigComplete(out, it->second.job, pos, it->second.old_tile, new_tile, worker);
}
}
@@ -370,7 +367,7 @@ static void add_walls_to_burrows(color_ostream &out, std::vector &b
}
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos,
- df::tiletype old_tile, df::tiletype new_tile)
+ df::tiletype old_tile, df::tiletype new_tile, df::unit *worker)
{
if (!isWalkable(new_tile))
return;
@@ -390,9 +387,11 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
return;
MapExtras::MapCache mc;
+ bool changed = false;
if (!isWalkable(old_tile))
{
+ changed = true;
add_walls_to_burrows(out, to_grow, mc, pos+df::coord(-1,-1,0), pos+df::coord(1,1,0));
if (isWalkableUp(new_tile))
@@ -407,6 +406,7 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
if (LowPassable(new_tile) && !LowPassable(old_tile))
{
+ changed = true;
add_to_burrows(to_grow, pos-df::coord(0,0,1));
if (tileShape(new_tile) == tiletype_shape::RAMP_TOP)
@@ -415,6 +415,9 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
pos+df::coord(-1,-1,-1), pos+df::coord(1,1,-1));
}
}
+
+ if (changed && worker && !worker->job.current_job)
+ Job::checkDesignationsNow();
}
static void renameBurrow(color_ostream &out, df::burrow *burrow, std::string name)
diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp
index f644398f5..c4a925964 100644
--- a/plugins/liquids.cpp
+++ b/plugins/liquids.cpp
@@ -418,10 +418,7 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setDesignationAt(*iter,a);
Block * b = mcache.BlockAt((*iter)/16);
- DFHack::t_blockflags bf = b->BlockFlags();
- bf.bits.update_liquid = true;
- bf.bits.update_liquid_twice = true;
- b->setBlockFlags(bf);
+ b->enableBlockUpdates(true);
iter++;
}
@@ -448,7 +445,8 @@ command_result df_liquids_execute(color_ostream &out)
DFHack::DFCoord current = *iter; // current tile coord
DFHack::DFCoord curblock = current /16; // current block coord
// check if the block is actually there
- if(!mcache.BlockAt(curblock))
+ auto block = mcache.BlockAt(curblock);
+ if(!block)
{
iter ++;
continue;
@@ -463,64 +461,77 @@ command_result df_liquids_execute(color_ostream &out)
}
if(mode != "flowbits")
{
+ unsigned old_amount = des.bits.flow_size;
+ unsigned new_amount = old_amount;
+ df::tile_liquid old_liquid = des.bits.liquid_type;
+ df::tile_liquid new_liquid = old_liquid;
+ // Compute new liquid type and amount
if(setmode == "s.")
{
- des.bits.flow_size = amount;
+ new_amount = amount;
}
else if(setmode == "s+")
{
- if(des.bits.flow_size < amount)
- des.bits.flow_size = amount;
+ if(old_amount < amount)
+ new_amount = amount;
}
else if(setmode == "s-")
{
- if (des.bits.flow_size > amount)
- des.bits.flow_size = amount;
+ if (old_amount > amount)
+ new_amount = amount;
}
- if(amount != 0 && mode == "magma")
+ if (mode == "magma")
+ new_liquid = tile_liquid::Magma;
+ else if (mode == "water")
+ new_liquid = tile_liquid::Water;
+ // Store new amount and type
+ des.bits.flow_size = new_amount;
+ des.bits.liquid_type = new_liquid;
+ // Compute temperature
+ if (!old_amount)
+ old_liquid = tile_liquid::Water;
+ if (!new_amount)
+ new_liquid = tile_liquid::Water;
+ if (old_liquid != new_liquid)
{
- des.bits.liquid_type = tile_liquid::Magma;
- mcache.setTemp1At(current,12000);
- mcache.setTemp2At(current,12000);
- }
- else if(amount != 0 && mode == "water")
- {
- des.bits.liquid_type = tile_liquid::Water;
- mcache.setTemp1At(current,10015);
- mcache.setTemp2At(current,10015);
- }
- else if(amount == 0 && (mode == "water" || mode == "magma"))
- {
- // reset temperature to sane default
- mcache.setTemp1At(current,10015);
- mcache.setTemp2At(current,10015);
+ if (new_liquid == tile_liquid::Water)
+ {
+ mcache.setTemp1At(current,10015);
+ mcache.setTemp2At(current,10015);
+ }
+ else
+ {
+ mcache.setTemp1At(current,12000);
+ mcache.setTemp2At(current,12000);
+ }
}
// mark the tile passable or impassable like the game does
- des.bits.flow_forbid = des.bits.flow_size &&
- (des.bits.liquid_type == tile_liquid::Magma || des.bits.flow_size > 3);
+ des.bits.flow_forbid = (new_liquid == tile_liquid::Magma || new_amount > 3);
mcache.setDesignationAt(current,des);
+ // request flow engine updates
+ block->enableBlockUpdates(new_amount != old_amount, new_liquid != old_liquid);
}
- seen_blocks.insert(mcache.BlockAt(current / 16));
+ seen_blocks.insert(block);
iter++;
}
set ::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end())
{
- DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+")
{
- bflags.bits.update_liquid = true;
- bflags.bits.update_liquid_twice = true;
- (*biter)->setBlockFlags(bflags);
+ (*biter)->enableBlockUpdates(true);
}
else if(flowmode == "f-")
{
- bflags.bits.update_liquid = false;
- bflags.bits.update_liquid_twice = false;
- (*biter)->setBlockFlags(bflags);
+ if (auto block = (*biter)->getRaw())
+ {
+ block->flags.bits.update_liquid = false;
+ block->flags.bits.update_liquid_twice = false;
+ }
}
else
{
+ auto bflags = (*biter)->BlockFlags();
out << "flow bit 1 = " << bflags.bits.update_liquid << endl;
out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl;
}
diff --git a/plugins/stonesense b/plugins/stonesense
index 15e3b726c..07e832b9d 160000
--- a/plugins/stonesense
+++ b/plugins/stonesense
@@ -1 +1 @@
-Subproject commit 15e3b726c3e68d2985aecd95d2a33bf4550caaa1
+Subproject commit 07e832b9d2b1862dc831c872a559d5704d3ad13e
diff --git a/scripts/devel/list-filters.lua b/scripts/devel/list-filters.lua
new file mode 100644
index 000000000..69fb57164
--- /dev/null
+++ b/scripts/devel/list-filters.lua
@@ -0,0 +1,96 @@
+-- List input items for the building currently being built.
+
+local dumper = require 'dumper'
+
+local function copy_flags(tgt,src,name)
+ local val = src[name]
+ if val.whole == 0 then
+ return
+ end
+ local out = {}
+ tgt[name] = out
+ for k,v in pairs(val) do
+ if v then
+ out[k] = v
+ end
+ end
+end
+
+local function copy_value(tgt,src,name,defval)
+ if src[name] ~= defval then
+ tgt[name] = src[name]
+ end
+end
+local function copy_enum(tgt,src,name,defval,ename,enum)
+ if src[name] ~= defval then
+ tgt[name] = ename..'.'..enum[src[name]]
+ end
+end
+
+local lookup = {}
+
+for i=df.job_item_vector_id._first_item,df.job_item_vector_id._last_item do
+ local id = df.job_item_vector_id.attrs[i].other
+ local ptr
+ if id == df.items_other_id.ANY then
+ ptr = df.global.world.items.all
+ elseif id == df.items_other_id.BAD then
+ ptr = df.global.world.items.bad
+ else
+ ptr = df.global.world.items.other[id]
+ end
+ if ptr then
+ local _,addr = df.sizeof(ptr)
+ lookup[addr] = 'df.job_item_vector_id.'..df.job_item_vector_id[i]
+ end
+end
+
+local function clone_filter(src,quantity)
+ local tgt = {}
+ src.flags2.allow_artifact = false
+ if quantity ~= 1 then
+ tgt.quantity = quantity
+ end
+ copy_enum(tgt, src, 'item_type', -1, 'df.item_type', df.item_type)
+ copy_value(tgt, src, 'item_subtype', -1)
+ copy_value(tgt, src, 'mat_type', -1)
+ copy_value(tgt, src, 'mat_index', -1)
+ copy_flags(tgt, src, 'flags1')
+ copy_flags(tgt, src, 'flags2')
+ copy_flags(tgt, src, 'flags3')
+ copy_value(tgt, src, 'reaction_class', '')
+ copy_value(tgt, src, 'has_material_reaction_product', '')
+ copy_value(tgt, src, 'metal_ore', -1)
+ copy_value(tgt, src, 'min_dimension', -1)
+ copy_enum(tgt, src, 'has_tool_use', -1, 'df.tool_uses', df.tool_uses)
+ local ptr = src.item_vector
+ if ptr and ptr ~= df.global.world.items.other[0] then
+ local _,addr = df.sizeof(ptr)
+ tgt.vector_id = lookup[addr]
+ end
+ return tgt
+end
+
+local function dump(name)
+ local out = {}
+ for i,v in ipairs(df.global.ui_build_selector.requirements) do
+ out[#out+1] = clone_filter(v.filter, v.count_required)
+ end
+
+ local fmt = dumper.DataDumper(out,name,false,1,4)
+ local out = string.gsub(fmt, '"', '')
+ print(out)
+end
+
+local itype = df.global.ui_build_selector.building_type
+local stype = df.global.ui_build_selector.building_subtype
+
+if itype == df.building_type.Workshop then
+ dump(' [df.workshop_type.'..df.workshop_type[stype]..'] = ')
+elseif itype == df.building_type.Furnace then
+ dump(' [df.furnace_type.'..df.furnace_type[stype]..'] = ')
+elseif itype == df.building_type.Trap then
+ dump(' [df.trap_type.'..df.trap_type[stype]..'] = ')
+else
+ dump(' [df.building_type.'..df.building_type[itype]..'] = ')
+end
diff --git a/scripts/lua-example.lua b/scripts/devel/lua-example.lua
similarity index 100%
rename from scripts/lua-example.lua
rename to scripts/devel/lua-example.lua
diff --git a/scripts/fix/item-occupancy.lua b/scripts/fix/item-occupancy.lua
new file mode 100644
index 000000000..b5466b7a8
--- /dev/null
+++ b/scripts/fix/item-occupancy.lua
@@ -0,0 +1,125 @@
+-- Verify item occupancy and block item list integrity.
+--
+-- Checks:
+-- 1) Item has flags.on_ground <=> it is in the correct block item list
+-- 2) A tile has items in block item list <=> it has occupancy.item
+-- 3) The block item lists are sorted.
+
+local utils = require 'utils'
+
+function check_block_items(fix)
+ local cnt = 0
+ local icnt = 0
+ local found = {}
+ local found_somewhere = {}
+
+ local should_fix = false
+ local can_fix = true
+
+ for _,block in ipairs(df.global.world.map.map_blocks) do
+ local itable = {}
+ local bx,by,bz = pos2xyz(block.map_pos)
+
+ -- Scan the block item vector
+ local last_id = nil
+ local resort = false
+
+ for _,id in ipairs(block.items) do
+ local item = df.item.find(id)
+ local ix,iy,iz = pos2xyz(item.pos)
+ local dx,dy,dz = ix-bx,iy-by,iz-bz
+
+ -- Check sorted order
+ if last_id and last_id >= id then
+ print(bx,by,bz,last_id,id,'block items not sorted')
+ resort = true
+ else
+ last_id = id
+ end
+
+ -- Check valid coordinates and flags
+ if not item.flags.on_ground then
+ print(bx,by,bz,id,dx,dy,'in block & not on ground')
+ elseif dx < 0 or dx >= 16 or dy < 0 or dy >= 16 or dz ~= 0 then
+ found_somewhere[id] = true
+ print(bx,by,bz,id,dx,dy,dz,'invalid pos')
+ can_fix = false
+ else
+ found[id] = true
+ itable[dx + dy*16] = true;
+
+ -- Check missing occupancy
+ if not block.occupancy[dx][dy].item then
+ print(bx,by,bz,dx,dy,'item & not occupied')
+ if fix then
+ block.occupancy[dx][dy].item = true
+ else
+ should_fix = true
+ end
+ end
+ end
+ end
+
+ -- Sort the vector if needed
+ if resort then
+ if fix then
+ utils.sort_vector(block.items)
+ else
+ should_fix = true
+ end
+ end
+
+ icnt = icnt + #block.items
+
+ -- Scan occupancy for spurious marks
+ for x=0,15 do
+ local ocx = block.occupancy[x]
+ for y=0,15 do
+ if ocx[y].item and not itable[x + y*16] then
+ print(bx,by,bz,x,y,'occupied & no item')
+ if fix then
+ ocx[y].item = false
+ else
+ should_fix = true
+ end
+ end
+ end
+ end
+
+ cnt = cnt + 256
+ end
+
+ -- Check if any items are missing from blocks
+ for _,item in ipairs(df.global.world.items.all) do
+ if item.flags.on_ground and not found[item.id] then
+ can_fix = false
+ if not found_somewhere[item.id] then
+ print(id,item.pos.x,item.pos.y,item.pos.z,'on ground & not in block')
+ end
+ end
+ end
+
+ -- Report
+ print(cnt.." tiles and "..icnt.." items checked.")
+
+ if should_fix and can_fix then
+ print("Use 'fix/item-occupancy --fix' to fix the listed problems.")
+ elseif should_fix then
+ print("The problems are too severe to be fixed by this script.")
+ end
+end
+
+local opt = ...
+local fix = false
+
+if opt then
+ if opt == '--fix' then
+ fix = true
+ else
+ dfhack.printerr('Invalid option: '..opt)
+ return
+ end
+end
+
+print("Checking item occupancy - this will take a few seconds.")
+check_block_items(fix)