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)