Merge remote-tracking branch 'angavrilov/master'

Conflicts:
	library/xml
develop
Kelly Martin 2012-05-12 19:49:27 -05:00
commit 86dc2d9855
20 changed files with 796 additions and 83 deletions

@ -666,6 +666,14 @@ Job module
Returns the unit performing the 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)`` * ``dfhack.job.is_equal(job1,job2)``
Compares important fields in the job and nested item structures. 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. 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)`` * ``dfhack.maps.getGlobalInitFeature(index)``
Returns the global feature object with the given 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 Emitted when a burrow might have been renamed either through
the game UI, or ``renameBurrow()``. 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 Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled. auto-growing burrows feature is enabled.

@ -912,6 +912,12 @@ The is_bright boolean actually seems to invert the brightness.</p>
<li><p class="first"><tt class="docutils literal">dfhack.job.getWorker(job)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.getWorker(job)</tt></p>
<p>Returns the unit performing the job.</p> <p>Returns the unit performing the job.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.job.checkBuildingsNow()</tt></p>
<p>Instructs the game to check buildings for jobs next frame and assign workers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.checkDesignationsNow()</tt></p>
<p>Instructs the game to check designations for jobs next frame and assign workers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.is_equal(job1,job2)</tt></p>
<p>Compares important fields in the job and nested item structures.</p> <p>Compares important fields in the job and nested item structures.</p>
</li> </li>
@ -1023,6 +1029,9 @@ Returns <em>false</em> in case of error.</p>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getRegionBiome(region_coord2d)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getRegionBiome(region_coord2d)</tt></p>
<p>Returns the biome info struct for the given global map region.</p> <p>Returns the biome info struct for the given global map region.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.maps.enableBlockUpdates(block[,flow,temperature])</span></tt></p>
<p>Enables updates for liquid flow or temperature, unless already active.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getGlobalInitFeature(index)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getGlobalInitFeature(index)</tt></p>
<p>Returns the global feature object with the given index.</p> <p>Returns the global feature object with the given index.</p>
</li> </li>
@ -1236,7 +1245,7 @@ module file is still necessary for <tt class="docutils literal">require</tt> to
<p>Emitted when a burrow might have been renamed either through <p>Emitted when a burrow might have been renamed either through
the game UI, or <tt class="docutils literal">renameBurrow()</tt>.</p> the game UI, or <tt class="docutils literal">renameBurrow()</tt>.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)</tt></p> <li><p class="first"><tt class="docutils literal">onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype,worker)</tt></p>
<p>Emitted when a tile might have been dug out. Only tracked if the <p>Emitted when a tile might have been dug out. Only tracked if the
auto-growing burrows feature is enabled.</p> auto-growing burrows feature is enabled.</p>
</li> </li>

@ -637,6 +637,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,printJobDetails), WRAPM(Job,printJobDetails),
WRAPM(Job,getHolder), WRAPM(Job,getHolder),
WRAPM(Job,getWorker), WRAPM(Job,getWorker),
WRAPM(Job,checkBuildingsNow),
WRAPM(Job,checkDesignationsNow),
WRAPN(is_equal, jobEqual), WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual), WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL } { NULL, NULL }
@ -753,6 +755,7 @@ static const luaL_Reg dfhack_items_funcs[] = {
static const LuaWrapper::FunctionReg dfhack_maps_module[] = { static const LuaWrapper::FunctionReg dfhack_maps_module[] = {
WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),
WRAPM(Maps, getRegionBiome), WRAPM(Maps, getRegionBiome),
WRAPM(Maps, enableBlockUpdates),
WRAPM(Maps, getGlobalInitFeature), WRAPM(Maps, getGlobalInitFeature),
WRAPM(Maps, getLocalInitFeature), WRAPM(Maps, getLocalInitFeature),
WRAPM(Maps, canWalkBetween), WRAPM(Maps, canWalkBetween),

@ -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_RETURN_TYPE((A1,A2,A3,A4,A5))
INSTANTIATE_WRAPPERS(5, (A1,A2,A3,A4,A5), (vA1,vA2,vA3,vA4,vA5), 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);) 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_TARGS
#undef FW_TARGSC #undef FW_TARGSC

@ -424,3 +424,17 @@ namespace DFHack {namespace Lua {
name##_event.invoke(out, 4); \ 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); \
} \
}

@ -57,6 +57,10 @@ namespace DFHack
DFHACK_EXPORT df::building *getHolder(df::job *job); DFHACK_EXPORT df::building *getHolder(df::job *job);
DFHACK_EXPORT df::unit *getWorker(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); DFHACK_EXPORT bool linkIntoWorld(df::job *job, bool new_id = true);
// lists jobs with ids >= *id_var, and sets *id_var = *job_next_id; // lists jobs with ids >= *id_var, and sets *id_var = *job_next_id;

@ -93,6 +93,12 @@ public:
Block(MapCache *parent, DFCoord _bcoord); Block(MapCache *parent, DFCoord _bcoord);
~Block(); ~Block();
DFCoord getCoord() { return bcoord; }
void enableBlockUpdates(bool flow = false, bool temp = false) {
Maps::enableBlockUpdates(block, flow, temp);
}
/* /*
* All coordinates are taken mod 16. * All coordinates are taken mod 16.
*/ */
@ -208,11 +214,8 @@ public:
dirty_designations = true; dirty_designations = true;
//printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y); //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y);
index_tile<df::tile_designation&>(designation,p) = des; index_tile<df::tile_designation&>(designation,p) = des;
if(des.bits.dig) if(des.bits.dig && block)
{ block->flags.bits.designated = true;
dirty_blockflags = true;
blockflags.bits.designated = true;
}
return true; return true;
} }
@ -236,15 +239,7 @@ public:
t_blockflags BlockFlags() t_blockflags BlockFlags()
{ {
return blockflags; return block ? block->flags : t_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;
} }
bool Write(); bool Write();
@ -273,7 +268,6 @@ private:
bool dirty_designations:1; bool dirty_designations:1;
bool dirty_tiles:1; bool dirty_tiles:1;
bool dirty_temperatures:1; bool dirty_temperatures:1;
bool dirty_blockflags:1;
bool dirty_occupancies:1; bool dirty_occupancies:1;
DFCoord bcoord; DFCoord bcoord;
@ -328,7 +322,6 @@ private:
designations40d designation; designations40d designation;
occupancies40d occupancy; occupancies40d occupancy;
t_blockflags blockflags;
t_temperatures temp1; t_temperatures temp1;
t_temperatures temp2; t_temperatures temp2;

@ -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); 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 /// sorts the block event vector into multiple vectors by type
/// mineral veins, what's under ice, blood smears and mud /// mineral veins, what's under ice, blood smears and mud
extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block,

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

@ -1,5 +1,7 @@
local _ENV = mkmodule('utils') local _ENV = mkmodule('utils')
local df = df
-- Comparator function -- Comparator function
function compare(a,b) function compare(a,b)
if a < b then if a < b then
@ -26,6 +28,43 @@ function compare_name(a,b)
end end
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. Sort items in data according to ordering.
@ -75,10 +114,7 @@ function make_sort_order(data,ordering)
end end
-- Make an order table -- Make an order table
local index = {} local index = make_index_sequence(size)
for i=1,size do
index[i] = i
end
-- Sort the ordering table -- Sort the ordering table
table.sort(index, function(ia,ib) table.sort(index, function(ia,ib)
@ -119,7 +155,7 @@ function assign(tgt,src)
df.assign(tgt, src) df.assign(tgt, src)
elseif type(tgt) == 'table' then elseif type(tgt) == 'table' then
for k,v in pairs(src) do 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] local cv = tgt[k]
if cv == nil then if cv == nil then
cv = {} cv = {}
@ -136,4 +172,131 @@ function assign(tgt,src)
return tgt return tgt
end 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 return _ENV

@ -709,8 +709,7 @@ static void linkBuilding(df::building *bld)
linkRooms(bld); linkRooms(bld);
if (process_jobs) Job::checkBuildingsNow();
*process_jobs = true;
} }
static void createDesign(df::building *bld, bool rough) 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::ui;
using df::global::world; using df::global::world;
using df::global::process_jobs;
using df::global::process_dig;
using df::global::ui_look_list; using df::global::ui_look_list;
CHECK_NULL_POINTER(bld); CHECK_NULL_POINTER(bld);
@ -952,8 +949,8 @@ bool Buildings::deconstruct(df::building *bld)
} }
} }
if (process_dig) *process_dig = true; Job::checkBuildingsNow();
if (process_jobs) *process_jobs = true; Job::checkDesignationsNow();
return true; return true;
} }

@ -256,6 +256,18 @@ df::unit *DFHack::Job::getWorker(df::job *job)
return NULL; 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) bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
{ {
using df::global::world; using df::global::world;

@ -54,6 +54,7 @@ using namespace std;
#include "df/world_region_details.h" #include "df/world_region_details.h"
#include "df/builtin_mats.h" #include "df/builtin_mats.h"
#include "df/block_square_event_grassst.h" #include "df/block_square_event_grassst.h"
#include "df/z_level_flags.h"
using namespace DFHack; using namespace DFHack;
using namespace df::enums; 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]; 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) df::feature_init *Maps::getGlobalInitFeature(int32_t index)
{ {
auto data = world->world_data; auto data = world->world_data;
@ -426,7 +451,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
dirty_designations = false; dirty_designations = false;
dirty_tiles = false; dirty_tiles = false;
dirty_temperatures = false; dirty_temperatures = false;
dirty_blockflags = false;
dirty_occupancies = false; dirty_occupancies = false;
valid = false; valid = false;
bcoord = _bcoord; bcoord = _bcoord;
@ -440,7 +464,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
{ {
COPY(designation, block->designation); COPY(designation, block->designation);
COPY(occupancy, block->occupancy); COPY(occupancy, block->occupancy);
blockflags = block->flags;
COPY(temp1, block->temperature_1); COPY(temp1, block->temperature_1);
COPY(temp2, block->temperature_2); COPY(temp2, block->temperature_2);
@ -449,7 +472,6 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent)
} }
else else
{ {
blockflags.whole = 0;
memset(designation,0,sizeof(designation)); memset(designation,0,sizeof(designation));
memset(occupancy,0,sizeof(occupancy)); memset(occupancy,0,sizeof(occupancy));
memset(temp1,0,sizeof(temp1)); memset(temp1,0,sizeof(temp1));
@ -634,11 +656,6 @@ bool MapExtras::Block::Write ()
{ {
if(!valid) return false; if(!valid) return false;
if(dirty_blockflags)
{
block->flags = blockflags;
dirty_blockflags = false;
}
if(dirty_designations) if(dirty_designations)
{ {
COPY(block->designation, designation); COPY(block->designation, designation);

@ -1 +1 @@
Subproject commit 970bb0ae9530e9c0522cb0ca8fe357815d72fab1 Subproject commit b20eba4170a2b7f52e465849e8853c3ecc978a8e

@ -153,10 +153,10 @@ static int next_job_id_save = 0;
static std::map<int,DigJob> diggers; static std::map<int,DigJob> diggers;
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos, 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, DEFINE_LUA_EVENT_5(onDigComplete, handle_dig_complete,
df::job_type, df::coord, df::tiletype, df::tiletype); df::job_type, df::coord, df::tiletype, df::tiletype, df::unit*);
static void detect_digging(color_ostream &out) 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) if (new_tile != it->second.old_tile)
{ {
onDigComplete(out, it->second.job, pos, it->second.old_tile, new_tile); onDigComplete(out, it->second.job, pos, it->second.old_tile, new_tile, worker);
//if (worker && !worker->job.current_job)
// worker->counters.think_counter = worker->counters.job_counter = 0;
} }
} }
@ -370,7 +367,7 @@ static void add_walls_to_burrows(color_ostream &out, std::vector<df::burrow*> &b
} }
static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord pos, 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)) if (!isWalkable(new_tile))
return; return;
@ -390,9 +387,11 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord
return; return;
MapExtras::MapCache mc; MapExtras::MapCache mc;
bool changed = false;
if (!isWalkable(old_tile)) 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)); add_walls_to_burrows(out, to_grow, mc, pos+df::coord(-1,-1,0), pos+df::coord(1,1,0));
if (isWalkableUp(new_tile)) 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)) if (LowPassable(new_tile) && !LowPassable(old_tile))
{ {
changed = true;
add_to_burrows(to_grow, pos-df::coord(0,0,1)); add_to_burrows(to_grow, pos-df::coord(0,0,1));
if (tileShape(new_tile) == tiletype_shape::RAMP_TOP) 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)); 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) static void renameBurrow(color_ostream &out, df::burrow *burrow, std::string name)

@ -418,10 +418,7 @@ command_result df_liquids_execute(color_ostream &out)
mcache.setDesignationAt(*iter,a); mcache.setDesignationAt(*iter,a);
Block * b = mcache.BlockAt((*iter)/16); Block * b = mcache.BlockAt((*iter)/16);
DFHack::t_blockflags bf = b->BlockFlags(); b->enableBlockUpdates(true);
bf.bits.update_liquid = true;
bf.bits.update_liquid_twice = true;
b->setBlockFlags(bf);
iter++; iter++;
} }
@ -448,7 +445,8 @@ command_result df_liquids_execute(color_ostream &out)
DFHack::DFCoord current = *iter; // current tile coord DFHack::DFCoord current = *iter; // current tile coord
DFHack::DFCoord curblock = current /16; // current block coord DFHack::DFCoord curblock = current /16; // current block coord
// check if the block is actually there // check if the block is actually there
if(!mcache.BlockAt(curblock)) auto block = mcache.BlockAt(curblock);
if(!block)
{ {
iter ++; iter ++;
continue; continue;
@ -463,64 +461,77 @@ command_result df_liquids_execute(color_ostream &out)
} }
if(mode != "flowbits") 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.") if(setmode == "s.")
{ {
des.bits.flow_size = amount; new_amount = amount;
} }
else if(setmode == "s+") else if(setmode == "s+")
{ {
if(des.bits.flow_size < amount) if(old_amount < amount)
des.bits.flow_size = amount; new_amount = amount;
} }
else if(setmode == "s-") else if(setmode == "s-")
{ {
if (des.bits.flow_size > amount) if (old_amount > amount)
des.bits.flow_size = 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)
{
if (new_liquid == tile_liquid::Water)
{ {
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.setTemp1At(current,10015);
mcache.setTemp2At(current,10015); mcache.setTemp2At(current,10015);
} }
else if(amount == 0 && (mode == "water" || mode == "magma")) else
{ {
// reset temperature to sane default mcache.setTemp1At(current,12000);
mcache.setTemp1At(current,10015); mcache.setTemp2At(current,12000);
mcache.setTemp2At(current,10015); }
} }
// mark the tile passable or impassable like the game does // mark the tile passable or impassable like the game does
des.bits.flow_forbid = des.bits.flow_size && des.bits.flow_forbid = (new_liquid == tile_liquid::Magma || new_amount > 3);
(des.bits.liquid_type == tile_liquid::Magma || des.bits.flow_size > 3);
mcache.setDesignationAt(current,des); 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++; iter++;
} }
set <Block *>::iterator biter = seen_blocks.begin(); set <Block *>::iterator biter = seen_blocks.begin();
while (biter != seen_blocks.end()) while (biter != seen_blocks.end())
{ {
DFHack::t_blockflags bflags = (*biter)->BlockFlags();
if(flowmode == "f+") if(flowmode == "f+")
{ {
bflags.bits.update_liquid = true; (*biter)->enableBlockUpdates(true);
bflags.bits.update_liquid_twice = true;
(*biter)->setBlockFlags(bflags);
} }
else if(flowmode == "f-") else if(flowmode == "f-")
{ {
bflags.bits.update_liquid = false; if (auto block = (*biter)->getRaw())
bflags.bits.update_liquid_twice = false; {
(*biter)->setBlockFlags(bflags); block->flags.bits.update_liquid = false;
block->flags.bits.update_liquid_twice = false;
}
} }
else else
{ {
auto bflags = (*biter)->BlockFlags();
out << "flow bit 1 = " << bflags.bits.update_liquid << endl; out << "flow bit 1 = " << bflags.bits.update_liquid << endl;
out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl; out << "flow bit 2 = " << bflags.bits.update_liquid_twice << endl;
} }

@ -1 +1 @@
Subproject commit 15e3b726c3e68d2985aecd95d2a33bf4550caaa1 Subproject commit 07e832b9d2b1862dc831c872a559d5704d3ad13e

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

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