From 035d65536bf386632f3cf6e782ffa6abbea245cd Mon Sep 17 00:00:00 2001 From: Caldfir Date: Sun, 22 Apr 2012 18:51:27 -0700 Subject: [PATCH 01/27] Added in job loading to t_unit for stonesense. --- library/modules/Units.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 8b44d4cf9..a50350ea7 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -49,6 +49,7 @@ using namespace std; #include "df/world.h" #include "df/ui.h" +#include "df/job.h" #include "df/unit_inventory_item.h" #include "df/unit_soul.h" #include "df/nemesis_record.h" @@ -210,24 +211,18 @@ void Units::CopyCreature(df::unit * source, t_unit & furball) } } */ - /* - furball.current_job.occupationPtr = p->readDWord (addr_cr + offs.current_job_offset); - if(furball.current_job.occupationPtr) - { - furball.current_job.active = true; - furball.current_job.jobType = p->readByte (furball.current_job.occupationPtr + offs.job_type_offset ); - furball.current_job.jobId = p->readWord (furball.current_job.occupationPtr + offs.job_id_offset); - } - else + if(source->job.current_job == NULL) { furball.current_job.active = false; } - */ - // no jobs for now... + else { - furball.current_job.active = false; + furball.current_job.active = true; + furball.current_job.jobType = source->job.current_job->job_type; + furball.current_job.jobId = source->job.current_job->id; } } + int32_t Units::FindIndexById(int32_t creature_id) { return df::unit::binsearch_index(world->units.all, creature_id); From 191071beb6730f9575b737fdae39db6572ebd3f4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 8 May 2012 12:55:06 +0400 Subject: [PATCH 02/27] Add more lua scripts. --- library/lua/dumper.lua | 234 ++++++++++++++++++++++++++++ library/lua/utils.lua | 173 +++++++++++++++++++- scripts/devel/list-filters.lua | 96 ++++++++++++ scripts/{ => devel}/lua-example.lua | 0 scripts/fix/item-occupancy.lua | 125 +++++++++++++++ 5 files changed, 623 insertions(+), 5 deletions(-) create mode 100644 library/lua/dumper.lua create mode 100644 scripts/devel/list-filters.lua rename scripts/{ => devel}/lua-example.lua (100%) create mode 100644 scripts/fix/item-occupancy.lua 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..68f5731a4 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_replace(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/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) From d6813c7690f378ba58db6ce51a26b172b91524e9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 8 May 2012 19:08:34 +0400 Subject: [PATCH 03/27] Change xml submodule pointer. --- library/lua/utils.lua | 2 +- library/xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 68f5731a4..96ab2b190 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -290,7 +290,7 @@ function insert_sorted(vector,item,field,cmp) end -- Binary search, then insert or overwrite -function insert_or_replace(vector,item,field,cmp) +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 diff --git a/library/xml b/library/xml index 5707a6aa0..b684cb6a5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 5707a6aa0c035348c8769058ed77b320f9b1436c +Subproject commit b684cb6a5ae02d33534367cfe54c861c50a41ceb From af3e38909301439748713c0d6acd35c9942f4f42 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 12 May 2012 18:50:22 +0400 Subject: [PATCH 04/27] Update submodules. --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index b684cb6a5..b20eba417 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b684cb6a5ae02d33534367cfe54c861c50a41ceb +Subproject commit b20eba4170a2b7f52e465849e8853c3ecc978a8e 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 From 60bb486abac5d79f0fc9053739b6ec52dfa8519f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 12 May 2012 20:12:09 +0400 Subject: [PATCH 05/27] Add api for enabling liquid and temperature updates for blocks. Now updates also have to be enabled for the z level to work. --- LUA_API.rst | 12 +++++ Lua API.html | 9 ++++ library/LuaApi.cpp | 3 ++ library/include/modules/Job.h | 4 ++ library/include/modules/MapCache.h | 25 ++++----- library/include/modules/Maps.h | 3 ++ library/modules/Buildings.cpp | 9 ++-- library/modules/Job.cpp | 12 +++++ library/modules/Maps.cpp | 33 +++++++++--- plugins/liquids.cpp | 83 +++++++++++++++++------------- 10 files changed, 127 insertions(+), 66 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index e20b946d8..093dd943c 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. diff --git a/Lua API.html b/Lua API.html index 7aa8e651a..b6242712a 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.

  • 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/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/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/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; } From ccbae583653f69bdd208868cae825a854da4b29f Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sat, 12 May 2012 11:21:12 -0500 Subject: [PATCH 06/27] Set default beekeeper max to 1 in autolabor (see http://www.bay12games.com/dwarves/mantisbt/view.php?id=3981) --- plugins/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 2eaa27b2b..7d5915694 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -452,7 +452,7 @@ static const struct labor_default default_labor_infos[] = { /* POTTERY */ {AUTOMATIC, false, 1, 200, 0}, /* GLAZING */ {AUTOMATIC, false, 1, 200, 0}, /* PRESSING */ {AUTOMATIC, false, 1, 200, 0}, - /* BEEKEEPING */ {AUTOMATIC, false, 1, 200, 0}, + /* BEEKEEPING */ {AUTOMATIC, false, 1, 1, 0}, // reduce risk of stuck beekeepers (see http://www.bay12games.com/dwarves/mantisbt/view.php?id=3981) /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, }; From c6b52067bd90a2f7832a94a74cfd0e4ec0a8641c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 12 May 2012 20:54:26 +0400 Subject: [PATCH 07/27] Request designation rescan in auto-growing burrows. This improves performance of burrowed miners digging 1-wide tunnels. --- LUA_API.rst | 2 +- Lua API.html | 2 +- library/include/DataFuncs.h | 17 +++++++++++++++++ library/include/LuaTools.h | 14 ++++++++++++++ plugins/burrows.cpp | 19 +++++++++++-------- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 093dd943c..d8d53a4d1 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1066,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 b6242712a..846299a35 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1245,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/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/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) From 87ec1de891625e9dc68924c570ede0fe23b4b6b7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 13 May 2012 13:58:41 +0400 Subject: [PATCH 08/27] Improve lua api for tile biome access. --- LUA_API.rst | 6 +++- Lua API.html | 5 +++- library/LuaApi.cpp | 52 +++++++++++++++++++++++++++++++++- library/include/LuaTools.h | 8 ++++-- library/include/modules/Maps.h | 12 ++++++++ library/lua/dfhack.lua | 8 ++++-- library/modules/Maps.cpp | 47 +++++++++++++++++++++++------- 7 files changed, 120 insertions(+), 18 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index d8d53a4d1..bb1ef803e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -807,7 +807,7 @@ Maps module Returns a map block object for given df::coord or x,y,z in local tile coordinates. -* ``dfhack.maps.getRegionBiome(region_coord2d)`` +* ``dfhack.maps.getRegionBiome(region_coord2d)``, or ``getRegionBiome(x,y)`` Returns the biome info struct for the given global map region. @@ -823,6 +823,10 @@ Maps module Returns the local feature object with the given region coords and index. +* ``dfhack.maps.getTileBiomeRgn(coords)``, or ``getTileBiomeRgn(x,y,z)`` + + Returns *x, y* for use with ``getRegionBiome``. + * ``dfhack.maps.canWalkBetween(pos1, pos2)`` Checks if a dwarf may be able to walk between the two tiles, diff --git a/Lua API.html b/Lua API.html index 846299a35..3e023ebd3 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1026,7 +1026,7 @@ Returns false in case of error.

  • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

    Returns a map block object for given df::coord or x,y,z in local tile coordinates.

  • -
  • dfhack.maps.getRegionBiome(region_coord2d)

    +
  • dfhack.maps.getRegionBiome(region_coord2d), or getRegionBiome(x,y)

    Returns the biome info struct for the given global map region.

  • dfhack.maps.enableBlockUpdates(block[,flow,temperature])

    @@ -1038,6 +1038,9 @@ Returns false in case of error.

  • dfhack.maps.getLocalInitFeature(region_coord2d,index)

    Returns the local feature object with the given region coords and index.

  • +
  • dfhack.maps.getTileBiomeRgn(coords), or getTileBiomeRgn(x,y,z)

    +

    Returns x, y for use with getRegionBiome.

    +
  • dfhack.maps.canWalkBetween(pos1, pos2)

    Checks if a dwarf may be able to walk between the two tiles, using a pathfinding cache maintained by the game. Note that diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1baa4045d..529bdd077 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -94,6 +94,26 @@ void Lua::Push(lua_State *state, const Units::NoblePosition &pos) lua_setfield(state, -2, "position"); } +void Lua::Push(lua_State *state, df::coord pos) +{ + lua_createtable(state, 0, 3); + lua_pushinteger(state, pos.x); + lua_setfield(state, -2, "x"); + lua_pushinteger(state, pos.y); + lua_setfield(state, -2, "y"); + lua_pushinteger(state, pos.z); + lua_setfield(state, -2, "z"); +} + +void Lua::Push(lua_State *state, df::coord2d pos) +{ + lua_createtable(state, 0, 2); + lua_pushinteger(state, pos.x); + lua_setfield(state, -2, "x"); + lua_pushinteger(state, pos.y); + lua_setfield(state, -2, "y"); +} + int Lua::PushPosXYZ(lua_State *state, df::coord pos) { if (!pos.isValid()) @@ -110,6 +130,21 @@ int Lua::PushPosXYZ(lua_State *state, df::coord pos) } } +int Lua::PushPosXY(lua_State *state, df::coord2d pos) +{ + if (!pos.isValid()) + { + lua_pushnil(state); + return 1; + } + else + { + lua_pushinteger(state, pos.x); + lua_pushinteger(state, pos.y); + return 2; + } +} + static df::coord2d CheckCoordXY(lua_State *state, int base, bool vararg = false) { df::coord2d p; @@ -754,7 +789,6 @@ 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), @@ -769,8 +803,24 @@ static int maps_getTileBlock(lua_State *L) return 1; } +static int maps_getRegionBiome(lua_State *L) +{ + auto pos = CheckCoordXY(L, 1, true); + Lua::PushDFObject(L, Maps::getRegionBiome(pos)); + return 1; +} + +static int maps_getTileBiomeRgn(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + Lua::PushPosXY(L, Maps::getTileBiomeRgn(pos)); + return 1; +} + static const luaL_Reg dfhack_maps_funcs[] = { { "getTileBlock", maps_getTileBlock }, + { "getRegionBiome", maps_getRegionBiome }, + { "getTileBiomeRgn", maps_getTileBiomeRgn }, { NULL, NULL } }; diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index a52db2572..d3c7a65d2 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -264,11 +264,14 @@ namespace DFHack {namespace Lua { inline void Push(lua_State *state, bool value) { lua_pushboolean(state, value); } + inline void Push(lua_State *state, const char *str) { + lua_pushstring(state, str); + } inline void Push(lua_State *state, const std::string &str) { lua_pushlstring(state, str.data(), str.size()); } - inline void Push(lua_State *state, df::coord &obj) { PushDFObject(state, &obj); } - inline void Push(lua_State *state, df::coord2d &obj) { PushDFObject(state, &obj); } + DFHACK_EXPORT void Push(lua_State *state, df::coord obj); + DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); void Push(lua_State *state, const Units::NoblePosition &pos); template inline void Push(lua_State *state, T *ptr) { PushDFObject(state, ptr); @@ -293,6 +296,7 @@ namespace DFHack {namespace Lua { } DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); + DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); DFHACK_EXPORT bool IsCoreContext(lua_State *state); diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index c6dc7c404..e63eef733 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -255,8 +255,20 @@ inline df::tile_occupancy *getTileOccupancy(df::coord pos) { return getTileOccupancy(pos.x, pos.y, pos.z); } +/** + * Returns biome info about the specified world region. + */ DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos); +/** + * Returns biome world region coordinates for the given tile within given block. + */ +DFHACK_EXPORT df::coord2d getBlockTileBiomeRgn(df::map_block *block, df::coord2d pos); + +inline df::coord2d getTileBiomeRgn(df::coord pos) { + return getBlockTileBiomeRgn(getTileBlock(pos), 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); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index c0ba64375..926600c0f 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -111,9 +111,11 @@ function copyall(table) end function pos2xyz(pos) - local x = pos.x - if x and x ~= -30000 then - return x, pos.y, pos.z + if pos then + local x = pos.x + if x and x ~= -30000 then + return x, pos.y, pos.z + end end end diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index a8b7945d1..3160af75e 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -369,6 +369,39 @@ bool Maps::RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square return false; } +static df::coord2d biome_offsets[9] = { + df::coord2d(-1,-1), df::coord2d(0,-1), df::coord2d(1,-1), + df::coord2d(-1,0), df::coord2d(0,0), df::coord2d(1,0), + df::coord2d(-1,1), df::coord2d(0,1), df::coord2d(1,1) +}; + +inline df::coord2d getBiomeRgnPos(df::coord2d base, int idx) +{ + auto r = base + biome_offsets[idx]; + + int world_width = world->world_data->world_width; + int world_height = world->world_data->world_height; + + return df::coord2d(clip_range(r.x,0,world_width-1),clip_range(r.y,0,world_height-1)); +} + +df::coord2d Maps::getBlockTileBiomeRgn(df::map_block *block, df::coord2d pos) +{ + if (!block || !world->world_data) + return df::coord2d(); + + auto des = MapExtras::index_tile(block->designation,pos); + unsigned idx = des.bits.biome; + if (idx < 9) + { + idx = block->region_offset[idx]; + if (idx < 9) + return getBiomeRgnPos(block->region_pos, idx); + } + + return df::coord2d(); +} + /* * Layer geology */ @@ -386,20 +419,14 @@ bool Maps::ReadGeology(vector > *layer_mats, vector (*geoidx)[i] = df::coord2d(-30000,-30000); } - int world_width = world->world_data->world_width; - int world_height = world->world_data->world_height; + // regionX is in embark squares + // regionX/16 is in 16x16 embark square regions + df::coord2d map_region(world->map.region_x / 16, world->map.region_y / 16); // iterate over 8 surrounding regions + local region for (int i = eNorthWest; i < eBiomeCount; i++) { - // check against worldmap boundaries, fix if needed - // regionX is in embark squares - // regionX/16 is in 16x16 embark square regions - // i provides -1 .. +1 offset from the current region - int bioRX = world->map.region_x / 16 + ((i % 3) - 1); - int bioRY = world->map.region_y / 16 + ((i / 3) - 1); - - df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRY,0,world_height-1)); + df::coord2d rgn_pos = getBiomeRgnPos(map_region, i); (*geoidx)[i] = rgn_pos; From 642a625586b9d40440bd3ce05d827f707ccce5ac Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 13 May 2012 18:39:00 +0400 Subject: [PATCH 09/27] Support custom buildings in dfhack.buildings.getFiltersByType. Also document it and constructBuilding in Lua API docs. --- LUA_API.rst | 74 ++++++++++++++++++++++++++++++++ Lua API.html | 63 +++++++++++++++++++++++++++ library/lua/dfhack/buildings.lua | 65 ++++++++++++++++++++++++---- library/lua/utils.lua | 62 ++++++++++++++++++++++++++ scripts/devel/list-filters.lua | 58 ++++++++----------------- 5 files changed, 273 insertions(+), 49 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index bb1ef803e..c42785278 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -971,6 +971,80 @@ Low-level building creation functions; More high-level functions are implemented in lua and can be loaded by ``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``. +Among them are: + +* ``dfhack.buildings.getFiltersByType(argtable,type,subtype,custom)`` + + Returns a sequence of lua structures, describing input item filters + suitable for the specified building type, or *nil* if unknown or invalid. + The returned sequence is suitable for use as the ``job_items`` argument + of ``constructWithFilters``. + Uses tables defined in ``buildings.lua``. + + Argtable members ``material`` (the default name), ``bucket``, ``barrel``, + ``chain``, ``mechanism``, ``screw``, ``pipe``, ``anvil``, ``weapon`` are used to + augment the basic attributes with more detailed information if the + building has input items with the matching name (see the tables for naming details). + Note that it is impossible to *override* any properties this way, only supply those that + are not mentioned otherwise; one exception is that flags2.non_economic + is automatically cleared if an explicit material is specified. + +* ``dfhack.buildings.constructBuilding{...}`` + + Creates a building in one call, using options contained + in the argument table. Returns the building, or *nil, error*. + + **NOTE:** Despite the name, unless the building is abstract, + the function creates it in an 'unconstructed' stage, with + a queued in-game job that will actually construct it. I.e. + the function replicates programmatically what can be done + through the construct building menu in the game ui, except + that it does less environment constraint checking. + + The following options can be used: + + - ``pos = coordinates``, or ``x = ..., y = ..., z = ...`` + + Mandatory. Specifies the left upper corner of the building. + + - ``type = df.building_type.FOO, subtype = ..., custom = ...`` + + Mandatory. Specifies the type of the building. Obviously, subtype + and custom are only expected if the type requires them. + + - ``fields = { ... }`` + + Initializes fields of the building object after creation with ``df.assign``. + + - ``width = ..., height = ..., direction = ...`` + + Sets size and orientation of the building. If it is + fixed-size, specified dimensions are ignored. + + - ``full_rectangle = true`` + + For buildings like stockpiles or farm plots that can normally + accomodate individual tile exclusion, forces an error if any + tiles within the specified width*height are obstructed. + + - ``items = { item, item ... }``, or ``filters = { {...}, {...}... }`` + + Specifies explicit items or item filters to use in construction. + It is the job of the user to ensure they are correct for the building type. + + - ``abstract = true`` + + Specifies that the building is abstract and does not require construction. + Required for stockpiles and civzones; an error otherwise. + + - ``material = {...}, mechanism = {...}, ...`` + + If none of ``items``, ``filter``, or ``abstract`` is used, + the function uses ``getFiltersByType`` to compute the input + item filters, and passes the argument table through. If no filters + can be determined this way, ``constructBuilding`` throws an error. + + Constructions module -------------------- diff --git a/Lua API.html b/Lua API.html index 3e023ebd3..ed2e9f6a9 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1163,6 +1163,69 @@ Returns true if the building was destroyed and deallocated immediately.

    More high-level functions are implemented in lua and can be loaded by require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

    +

    Among them are:

    +
      +
    • dfhack.buildings.getFiltersByType(argtable,type,subtype,custom)

      +

      Returns a sequence of lua structures, describing input item filters +suitable for the specified building type, or nil if unknown or invalid. +The returned sequence is suitable for use as the job_items argument +of constructWithFilters. +Uses tables defined in buildings.lua.

      +

      Argtable members material (the default name), bucket, barrel, +chain, mechanism, screw, pipe, anvil, weapon are used to +augment the basic attributes with more detailed information if the +building has input items with the matching name (see the tables for naming details). +Note that it is impossible to override any properties this way, only supply those that +are not mentioned otherwise; one exception is that flags2.non_economic +is automatically cleared if an explicit material is specified.

      +
    • +
    • dfhack.buildings.constructBuilding{...}

      +

      Creates a building in one call, using options contained +in the argument table. Returns the building, or nil, error.

      +

      NOTE: Despite the name, unless the building is abstract, +the function creates it in an 'unconstructed' stage, with +a queued in-game job that will actually construct it. I.e. +the function replicates programmatically what can be done +through the construct building menu in the game ui, except +that it does less environment constraint checking.

      +

      The following options can be used:

      +
        +
      • pos = coordinates, or x = ..., y = ..., z = ...

        +

        Mandatory. Specifies the left upper corner of the building.

        +
      • +
      • type = df.building_type.FOO, subtype = ..., custom = ...

        +

        Mandatory. Specifies the type of the building. Obviously, subtype +and custom are only expected if the type requires them.

        +
      • +
      • fields = { ... }

        +

        Initializes fields of the building object after creation with df.assign.

        +
      • +
      • width = ..., height = ..., direction = ...

        +

        Sets size and orientation of the building. If it is +fixed-size, specified dimensions are ignored.

        +
      • +
      • full_rectangle = true

        +

        For buildings like stockpiles or farm plots that can normally +accomodate individual tile exclusion, forces an error if any +tiles within the specified width*height are obstructed.

        +
      • +
      • items = { item, item ... }, or filters = { {...}, {...}... }

        +

        Specifies explicit items or item filters to use in construction. +It is the job of the user to ensure they are correct for the building type.

        +
      • +
      • abstract = true

        +

        Specifies that the building is abstract and does not require construction. +Required for stockpiles and civzones; an error otherwise.

        +
      • +
      • material = {...}, mechanism = {...}, ...

        +

        If none of items, filter, or abstract is used, +the function uses getFiltersByType to compute the input +item filters, and passes the argument table through. If no filters +can be determined this way, constructBuilding throws an error.

        +
      • +
      +
    • +

    Constructions module

    diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index eb0a6a5b5..ad82da89a 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -4,7 +4,32 @@ local buildings = dfhack.buildings local utils = require 'utils' ---[[ Building input material tables. ]] +-- Uninteresting values for filter attributes when reading them from DF memory. +-- Differs from the actual defaults of the job_item constructor in allow_artifact. + +buildings.input_filter_defaults = { + item_type = -1, + item_subtype = -1, + mat_type = -1, + mat_index = -1, + flags1 = {}, + -- Instead of noting those that allow artifacts, mark those that forbid them. + -- Leaves actually enabling artifacts to the discretion of the API user, + -- which is the right thing because unlike the game UI these filters are + -- used in a way that does not give the user a chance to choose manually. + flags2 = { allow_artifact = true }, + flags3 = {}, + flags4 = 0, + flags5 = 0, + reaction_class = '', + has_material_reaction_product = '', + metal_ore = -1, + min_dimension = -1, + has_tool_use = -1, + quantity = 1 +} + +--[[ Building input material table. ]] local building_inputs = { [df.building_type.Chair] = { { item_type=df.item_type.CHAIR, vector_id=df.job_item_vector_id.CHAIR } }, @@ -12,7 +37,6 @@ local building_inputs = { [df.building_type.Table] = { { item_type=df.item_type.TABLE, vector_id=df.job_item_vector_id.TABLE } }, [df.building_type.Coffin] = { { item_type=df.item_type.COFFIN, vector_id=df.job_item_vector_id.COFFIN } }, [df.building_type.FarmPlot] = { }, - [df.building_type.Furnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, [df.building_type.TradeDepot] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } }, [df.building_type.Door] = { { item_type=df.item_type.DOOR, vector_id=df.job_item_vector_id.DOOR } }, [df.building_type.Floodgate] = { @@ -40,7 +64,6 @@ local building_inputs = { vector_id=df.job_item_vector_id.ARMORSTAND } }, - [df.building_type.Workshop] = { { flags2={ building_material=true, non_economic=true } } }, [df.building_type.Cabinet] = { { item_type=df.item_type.CABINET, vector_id=df.job_item_vector_id.CABINET } }, @@ -89,7 +112,7 @@ local building_inputs = { [df.building_type.ArcheryTarget] = { { flags2={ building_material=true, non_economic=true } } }, [df.building_type.Chain] = { { item_type=df.item_type.CHAIN, vector_id=df.job_item_vector_id.CHAIN } }, [df.building_type.Cage] = { { item_type=df.item_type.CAGE, vector_id=df.job_item_vector_id.CAGE } }, - [df.building_type.Weapon] = { { vector_id=df.job_item_vector_id.ANY_SPIKE } }, + [df.building_type.Weapon] = { { name='weapon', vector_id=df.job_item_vector_id.ANY_SPIKE } }, [df.building_type.ScrewPump] = { { item_type=df.item_type.BLOCKS, @@ -158,6 +181,8 @@ local building_inputs = { [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } } } +--[[ Furnace building input material table. ]] + local furnace_inputs = { [df.furnace_type.WoodFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, [df.furnace_type.Smelter] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, @@ -168,6 +193,8 @@ local furnace_inputs = { [df.furnace_type.MagmaKiln] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } } } +--[[ Workshop building input material table. ]] + local workshop_inputs = { [df.workshop_type.Carpenters] = { { flags2={ building_material=true, non_economic=true } } }, [df.workshop_type.Farmers] = { { flags2={ building_material=true, non_economic=true } } }, @@ -250,6 +277,8 @@ local workshop_inputs = { } } +--[[ Trap building input material table. ]] + local trap_inputs = { [df.trap_type.StoneFallTrap] = { { @@ -264,7 +293,10 @@ local trap_inputs = { item_type=df.item_type.TRAPPARTS, vector_id=df.job_item_vector_id.TRAPPARTS }, - { vector_id=df.job_item_vector_id.ANY_WEAPON } + { + name='weapon', + vector_id=df.job_item_vector_id.ANY_WEAPON + } }, [df.trap_type.Lever] = { { @@ -289,11 +321,28 @@ local trap_inputs = { } } +--[[ Functions for lookup in tables. ]] + +local function get_custom_inputs(custom) + local defn = df.building_def.find(custom) + if defn ~= nil then + return utils.clone_with_default(defn.build_items, buildings.input_filter_defaults) + end +end + local function get_inputs_by_type(type,subtype,custom) if type == df.building_type.Workshop then - return workshop_inputs[subtype] + if subtype == df.workshop_type.Custom then + return get_custom_inputs(custom) + else + return workshop_inputs[subtype] + end elseif type == df.building_type.Furnace then - return furnace_inputs[subtype] + if subtype == df.furnace_type.Custom then + return get_custom_inputs(custom) + else + return furnace_inputs[subtype] + end elseif type == df.building_type.Trap then return trap_inputs[subtype] else @@ -357,7 +406,7 @@ end -- Materials: items = { item, item ... }, -- OR - filter = { { ... }, { ... }... } + filters = { { ... }, { ... }... } -- OR abstract = true -- OR diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 96ab2b190..e67801f4f 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -221,6 +221,68 @@ function clone(obj,deep) end end +local function get_default(default,key,base) + if type(default) == 'table' then + local dv = default[key] + if dv == nil then + dv = default._default + end + if dv == nil then + dv = base + end + return dv + else + return default + end +end + +-- Copy the object as lua data structures, skipping values matching defaults. +function clone_with_default(obj,default,force) + local rv = nil + local function setrv(k,v) + if v ~= nil then + if rv == nil then + rv = {} + end + rv[k] = v + end + end + if default == nil then + return nil + elseif type(obj) == 'table' then + for k,v in pairs(obj) do + setrv(k, clone_with_default(v, get_default(default,k))) + end + elseif df.isvalid(obj) == 'ref' then + local kind = obj._kind + if kind == 'primitive' then + return clone_with_default(obj.value,default,force) + elseif kind == 'bitfield' then + for k,v in pairs(obj) do + setrv(k, clone_with_default(v, get_default(default,k,false))) + end + elseif kind == 'container' then + for k,v in ipairs(obj) do + setrv(k+1, clone_with_default(v, default, true)) + end + else -- struct + for k,v in pairs(obj) do + setrv(k, clone_with_default(v, get_default(default,k))) + end + end + elseif obj == default and not force then + return nil + elseif obj == nil then + return NULL + else + return obj + end + if force and rv == nil then + rv = {} + end + return rv +end + -- Sort a vector or lua table function sort_vector(vector,field,cmp) local fcmp = compare_field(field,cmp) diff --git a/scripts/devel/list-filters.lua b/scripts/devel/list-filters.lua index 69fb57164..87b9f24b1 100644 --- a/scripts/devel/list-filters.lua +++ b/scripts/devel/list-filters.lua @@ -1,43 +1,29 @@ -- List input items for the building currently being built. +-- +-- This is where the filters in lua/dfhack/buildings.lua come from. local dumper = require 'dumper' +local utils = require 'utils' +local buildings = require 'dfhack.buildings' -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]] +local function name_enum(tgt,name,ename,enum) + if tgt[name] ~= nil then + tgt[name] = ename..'.'..enum[tgt[name]] end end local lookup = {} +local items = df.global.world.items 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 + ptr = items.all elseif id == df.items_other_id.BAD then - ptr = df.global.world.items.bad + ptr = items.bad else - ptr = df.global.world.items.other[id] + ptr = items.other[id] end if ptr then local _,addr = df.sizeof(ptr) @@ -46,23 +32,12 @@ for i=df.job_item_vector_id._first_item,df.job_item_vector_id._last_item do end local function clone_filter(src,quantity) - local tgt = {} - src.flags2.allow_artifact = false + local tgt = utils.clone_with_default(src, buildings.input_filter_defaults, true) 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) + name_enum(tgt, 'item_type', 'df.item_type', df.item_type) + name_enum(tgt, 'has_tool_use', '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) @@ -78,8 +53,9 @@ local function dump(name) end local fmt = dumper.DataDumper(out,name,false,1,4) - local out = string.gsub(fmt, '"', '') - print(out) + fmt = string.gsub(fmt, '"(df%.[^"]+)"','%1') + fmt = string.gsub(fmt, '%s+$', '') + print(fmt) end local itype = df.global.ui_build_selector.building_type From e078c75737c51039b3e44f4e8540eb2de4f012db Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 14 May 2012 13:44:03 -0500 Subject: [PATCH 10/27] Track submodules --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index b20eba417..dc6e5b4b1 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b20eba4170a2b7f52e465849e8853c3ecc978a8e +Subproject commit dc6e5b4b16a5ba3a099d8d063807312ceb6a13d3 diff --git a/plugins/stonesense b/plugins/stonesense index 07e832b9d..15e3b726c 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 07e832b9d2b1862dc831c872a559d5704d3ad13e +Subproject commit 15e3b726c3e68d2985aecd95d2a33bf4550caaa1 From d4f98065683dfc9521ac0d86c27b8491027a980e Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 14 May 2012 17:30:21 -0500 Subject: [PATCH 11/27] Track library --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index dc6e5b4b1..8a9a76b9d 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit dc6e5b4b16a5ba3a099d8d063807312ceb6a13d3 +Subproject commit 8a9a76b9d0459992a62bf054a6df98483a3b14eb From 27cc2dc92cc7c2c5221a3e3234e8aadca343555e Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 14 May 2012 21:54:11 -0500 Subject: [PATCH 12/27] Add dwarf state table entries for the new jobs from .34.7 and .34.8. Also add a warning message for a dwarf who is doing an unknown job (instead of just reading random nonsense from off the end of the dwarf_states array). This code is backward-compatible (the extra job entries will simply be unused in pre-.8). --- plugins/autolabor.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index cd3232e64..5a588cdde 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -348,7 +348,12 @@ static const dwarf_state dwarf_states[] = { OTHER /* CauseTrouble */, OTHER /* DrinkBlood */, OTHER /* ReportCrime */, - OTHER /* ExecuteCriminal */ + OTHER /* ExecuteCriminal */, + BUSY /* TrainAnimal */, + BUSY /* CarveTrack */, + BUSY /* PushTrackVehicle */, + BUSY /* PlaceTrackVehicle */, + BUSY /* StoreItemInVehicle */ }; struct labor_info @@ -886,8 +891,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(job >= 0); assert(job < ARRAY_COUNT(dwarf_states)); */ - - dwarf_info[dwarf].state = dwarf_states[job]; + if (job >= 0 && job < ARRAY_COUNT(dwarf_states)) + dwarf_info[dwarf].state = dwarf_states[job]; + else + { + out.print("Dwarf %i \"%s\" has unknown job %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), job); + dwarf_info[dwarf].state = OTHER; + } } state_count[dwarf_info[dwarf].state]++; From e77c9dc730fdd94423319b744fb4162bd36e8377 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Tue, 15 May 2012 13:42:48 -0500 Subject: [PATCH 13/27] Add new PUSH_HAUL_VEHICLES labor to autolabor. This commit will only work with 0.34.08 (or later). --- library/xml | 2 +- plugins/autolabor.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index 8a9a76b9d..92dbeb626 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 8a9a76b9d0459992a62bf054a6df98483a3b14eb +Subproject commit 92dbeb6267a24a35babe3d5326801f2e8cfd22e1 diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 5a588cdde..d89ddd7c6 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -458,7 +458,8 @@ static const struct labor_default default_labor_infos[] = { /* GLAZING */ {AUTOMATIC, false, 1, 200, 0}, /* PRESSING */ {AUTOMATIC, false, 1, 200, 0}, /* BEEKEEPING */ {AUTOMATIC, false, 1, 1, 0}, // reduce risk of stuck beekeepers (see http://www.bay12games.com/dwarves/mantisbt/view.php?id=3981) - /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, + /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, + /* PUSH_HAUL_VEHICLES */ {HAULERS, false, 1, 200, 0} }; static const int responsibility_penalties[] = { @@ -1271,7 +1272,7 @@ command_result autolabor (color_ostream &out, std::vector & parame if (labor == df::enums::unit_labor::NONE) { - out.printerr("Could not find labor %s.", parameters[0].c_str()); + out.printerr("Could not find labor %s.\n", parameters[0].c_str()); return CR_WRONG_USAGE; } From dbd39af58a1eea630b6e390375517acb79b179f2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 16 May 2012 17:06:08 +0400 Subject: [PATCH 14/27] Support testing and modifying pending timeout callbacks. --- LUA_API.rst | 7 ++++ Lua API.html | 6 +++ library/LuaTools.cpp | 91 +++++++++++++++++++++++++++++--------------- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index c42785278..bd2d33cb0 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1087,6 +1087,13 @@ Core context specific functions: Returns the timer id, or *nil* if unsuccessful due to world being unloaded. +* ``dfhack.timeout_active(id[,new_callback])`` + + Returns the active callback with the given id, or *nil* + if inactive or nil id. If called with 2 arguments, replaces + the current callback with the given value, if still active. + Using ``timeout_active(id,nil)`` cancels the timer. + * ``dfhack.onStateChange.foo = function(code)`` Event. Receives the same codes as plugin_onstatechange in C++. diff --git a/Lua API.html b/Lua API.html index ed2e9f6a9..d44d82bab 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1265,6 +1265,12 @@ and cannot be queued until it is loaded again. Returns the timer id, or nil if unsuccessful due to world being unloaded.

  • +
  • dfhack.timeout_active(id[,new_callback])

    +

    Returns the active callback with the given id, or nil +if inactive or nil id. If called with 2 arguments, replaces +the current callback with the given value, if still active. +Using timeout_active(id,nil) cancels the timer.

    +
  • dfhack.onStateChange.foo = function(code)

    Event. Receives the same codes as plugin_onstatechange in C++.

  • diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 794cb52a6..f794f6b41 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -1429,6 +1429,7 @@ int dfhack_timeout(lua_State *L) using df::global::world; using df::global::enabler; + // Parse arguments lua_Number time = luaL_checknumber(L, 1); int mode = luaL_checkoption(L, 2, NULL, timeout_modes); luaL_checktype(L, 3, LUA_TFUNCTION); @@ -1440,6 +1441,7 @@ int dfhack_timeout(lua_State *L) return 1; } + // Compute timeout value switch (mode) { case 2: @@ -1454,12 +1456,13 @@ int dfhack_timeout(lua_State *L) default:; } - int id = next_timeout_id++; int delta = time; if (delta <= 0) luaL_error(L, "Invalid timeout: %d", delta); + // Queue the timeout + int id = next_timeout_id++; if (mode) tick_timers.insert(std::pair(world->frame_counter+delta, id)); else @@ -1473,20 +1476,44 @@ int dfhack_timeout(lua_State *L) return 1; } -static void cancel_tick_timers() +int dfhack_timeout_active(lua_State *L) +{ + int id = luaL_optint(L, 1, -1); + bool set_cb = (lua_gettop(L) >= 2); + lua_settop(L, 2); + if (!lua_isnil(L, 2)) + luaL_checktype(L, 2, LUA_TFUNCTION); + + if (id < 0) + { + lua_pushnil(L); + return 1; + } + + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + lua_rawgeti(L, 3, id); + if (set_cb && !lua_isnil(L, -1)) + { + lua_pushvalue(L, 2); + lua_rawseti(L, 3, id); + } + return 1; +} + +static void cancel_timers(std::multimap &timers) { using Lua::Core::State; Lua::StackUnwinder frame(State); lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); - for (auto it = tick_timers.begin(); it != tick_timers.end(); ++it) + for (auto it = timers.begin(); it != timers.end(); ++it) { lua_pushnil(State); lua_rawseti(State, frame[1], it->second); } - tick_timers.clear(); + timers.clear(); } void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { @@ -1496,7 +1523,7 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { { case SC_MAP_UNLOADED: case SC_WORLD_UNLOADED: - cancel_tick_timers(); + cancel_timers(tick_timers); break; default:; @@ -1506,40 +1533,40 @@ void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { Lua::InvokeEvent(out, State, (void*)onStateChange, 1); } -void DFHack::Lua::Core::onUpdate(color_ostream &out) +static void run_timers(color_ostream &out, lua_State *L, + std::multimap &timers, int table, int bound) { - using df::global::world; - - Lua::StackUnwinder frame(State); - lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); - - frame_idx++; - - while (!frame_timers.empty() && - frame_timers.begin()->first <= frame_idx) + while (!timers.empty() && timers.begin()->first <= bound) { - int id = frame_timers.begin()->second; - frame_timers.erase(frame_timers.begin()); + int id = timers.begin()->second; + timers.erase(timers.begin()); - lua_rawgeti(State, frame[1], id); - lua_pushnil(State); - lua_rawseti(State, frame[1], id); + lua_rawgeti(L, table, id); + + if (lua_isnil(L, -1)) + lua_pop(L, 1); + else + { + lua_pushnil(L); + lua_rawseti(L, table, id); - Lua::SafeCall(out, State, 0, 0); + Lua::SafeCall(out, L, 0, 0); + } } +} - while (!tick_timers.empty() && - tick_timers.begin()->first <= world->frame_counter) - { - int id = tick_timers.begin()->second; - tick_timers.erase(tick_timers.begin()); +void DFHack::Lua::Core::onUpdate(color_ostream &out) +{ + using df::global::world; - lua_rawgeti(State, frame[1], id); - lua_pushnil(State); - lua_rawseti(State, frame[1], id); + if (frame_timers.empty() && tick_timers.empty()) + return; - Lua::SafeCall(out, State, 0, 0); - } + Lua::StackUnwinder frame(State); + lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + + run_timers(out, State, frame_timers, frame[1], ++frame_idx); + run_timers(out, State, tick_timers, frame[1], world->frame_counter); } void DFHack::Lua::Core::Init(color_ostream &out) @@ -1562,6 +1589,8 @@ void DFHack::Lua::Core::Init(color_ostream &out) lua_pushcfunction(State, dfhack_timeout); lua_setfield(State, -2, "timeout"); + lua_pushcfunction(State, dfhack_timeout_active); + lua_setfield(State, -2, "timeout_active"); lua_pop(State, 1); } From 8185eec95c82ea5acb4f49144e6aab2306bf7cd8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 16 May 2012 17:46:44 +0400 Subject: [PATCH 15/27] Add two more development related lua scripts. --- scripts/devel/cmptiles.lua | 42 ++++++++++++++++++ scripts/devel/watch-minecarts.lua | 74 +++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 scripts/devel/cmptiles.lua create mode 100644 scripts/devel/watch-minecarts.lua diff --git a/scripts/devel/cmptiles.lua b/scripts/devel/cmptiles.lua new file mode 100644 index 000000000..8421038eb --- /dev/null +++ b/scripts/devel/cmptiles.lua @@ -0,0 +1,42 @@ +-- Lists and/or compares two tiletype material groups. +-- Usage: devel/cmptiles material1 [material2] + +local nmat1,nmat2=... +local mat1 = df.tiletype_material[nmat1] +local mat2 = df.tiletype_material[nmat2] + +local tmat1 = {} +local tmat2 = {} + +local attrs = df.tiletype.attrs + +for i=df.tiletype._first_item,df.tiletype._last_item do + local shape = df.tiletype_shape[attrs[i].shape] or '' + local variant = df.tiletype_variant[attrs[i].variant] or '' + local special = df.tiletype_special[attrs[i].special] or '' + local direction = attrs[i].direction or '' + local code = shape..':'..variant..':'..special..':'..direction + + if attrs[i].material == mat1 then + tmat1[code] = true + end + if attrs[i].material == mat2 then + tmat2[code] = true + end +end + +local function list_diff(n, t1, t2) + local lst = {} + for k,v in pairs(t1) do + if not t2[k] then + lst[#lst+1] = k + end + end + table.sort(lst) + for k,v in ipairs(lst) do + print(n, v) + end +end + +list_diff(nmat1,tmat1,tmat2) +list_diff(nmat2,tmat2,tmat1) diff --git a/scripts/devel/watch-minecarts.lua b/scripts/devel/watch-minecarts.lua new file mode 100644 index 000000000..166004d85 --- /dev/null +++ b/scripts/devel/watch-minecarts.lua @@ -0,0 +1,74 @@ +-- Logs minecart coordinates and speeds to console. + +last_stats = last_stats or {} + +function compare_one(vehicle) + local last = last_stats[vehicle.id] + local item = df.item.find(vehicle.item_id) + local ipos = item.pos + local new = { + ipos.x*100000 + vehicle.offset_x, vehicle.speed_x, + ipos.y*100000 + vehicle.offset_y, vehicle.speed_y, + ipos.z*100000 + vehicle.offset_z, vehicle.speed_z + } + + if (last == nil) or item.flags.on_ground then + local delta = { vehicle.id } + local show = (last == nil) + + for i=1,6 do + local rv = 0 + if last then + rv = last[i] + end + delta[i*2] = new[i]/100000 + local dv = new[i] - rv + delta[i*2+1] = dv/100000 + if dv ~= 0 then + show = true + end + end + + if show then + print(table.unpack(delta)) + end + end + + last_stats[vehicle.id] = new +end + +function compare_all() + local seen = {} + for _,v in ipairs(df.global.world.vehicles.all) do + seen[v.id] = true + compare_one(v) + end + for k,v in pairs(last_stats) do + if not seen[k] then + print(k,'DEAD') + end + end +end + +function start_timer() + if not dfhack.timeout_active(timeout_id) then + timeout_id = dfhack.timeout(1, 'ticks', function() + compare_all() + start_timer() + end); + if not timeout_id then + dfhack.printerr('Could not start timer in watch-minecarts') + end + end +end + +compare_all() + +local cmd = ... + +if cmd == 'start' then + start_timer() +elseif cmd == 'stop' then + dfhack.timeout_active(timeout_id, nil) +end + From 68c5d9b86c2a252f87f53804c3af94881c4ae39e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 16 May 2012 18:10:07 +0400 Subject: [PATCH 16/27] Stop Shift-Enter from being handled as if it was Shift-M. --- library/Core.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/Core.cpp b/library/Core.cpp index d7e4435ce..7f08f402f 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1180,6 +1180,18 @@ int Core::UnicodeAwareSym(const SDL::KeyboardEvent& ke) if( '0' <= ke.ksym.sym && ke.ksym.sym <= '9') return ke.ksym.sym; if(SDL::K_F1 <= ke.ksym.sym && ke.ksym.sym <= SDL::K_F12) return ke.ksym.sym; + // These keys are mapped to the same control codes as Ctrl-? + switch (ke.ksym.sym) { + case SDL::K_RETURN: + case SDL::K_KP_ENTER: + case SDL::K_TAB: + case SDL::K_ESCAPE: + case SDL::K_DELETE: + return ke.ksym.sym; + default: + break; + } + int unicode = ke.ksym.unicode; // convert Ctrl characters to their 0x40-0x5F counterparts: From cba3f8d61d66e27de2ea7ec9e2f4d51d3706087b Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Wed, 16 May 2012 11:28:21 -0500 Subject: [PATCH 17/27] Track xml --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 92dbeb626..b0d8e71dc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 92dbeb6267a24a35babe3d5326801f2e8cfd22e1 +Subproject commit b0d8e71dc9f6e697409bd999801097acdc57fcd8 From efdb709284d393938db6b60185ee3898cb068954 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 00:19:29 +0400 Subject: [PATCH 18/27] Support creating rollers and stops with dfhack.buildings.constructBuilding. --- library/lua/dfhack/buildings.lua | 18 ++++++++++++++++-- library/modules/Buildings.cpp | 17 ++++++++++++++++- library/xml | 2 +- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index ad82da89a..af9ad2605 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -178,7 +178,20 @@ local building_inputs = { }, [df.building_type.Slab] = { { item_type=df.item_type.SLAB } }, [df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } }, - [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } } + [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } }, + [df.building_type.Rollers] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + quantity=-1, + vector_id=df.job_item_vector_id.TRAPPARTS + }, + { + name='chain', + item_type=df.item_type.CHAIN, + vector_id=df.job_item_vector_id.CHAIN + } + } } --[[ Furnace building input material table. ]] @@ -318,7 +331,8 @@ local trap_inputs = { item_type=df.item_type.TRAPPARTS, vector_id=df.job_item_vector_id.TRAPPARTS } - } + }, + [df.trap_type.TrackStop] = { { flags2={ building_material=true, non_economic=true } } } } --[[ Functions for lookup in tables. ]] diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 65cf009f3..d2b3e3527 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -66,6 +66,7 @@ using namespace DFHack; #include "df/building_screw_pumpst.h" #include "df/building_water_wheelst.h" #include "df/building_wellst.h" +#include "df/building_rollersst.h" using namespace df::enums; using df::global::ui; @@ -289,6 +290,10 @@ bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d ¢er, makeOneDim(size, center, direction); return true; + case Rollers: + makeOneDim(size, center, (direction&1) == 0); + return true; + case WaterWheel: size = df::coord2d(3,3); makeOneDim(size, center, direction); @@ -592,6 +597,12 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) obj->direction = (df::screw_pump_direction)direction; break; } + case Rollers: + { + auto obj = (df::building_rollersst*)bld; + obj->direction = (df::screw_pump_direction)direction; + break; + } case Bridge: { auto obj = (df::building_bridgest*)bld; @@ -881,7 +892,11 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorquantity < 0) items[i]->quantity = computeMaterialAmount(bld); - job->job_items.push_back(items[i]); + /* The game picks up explicitly listed items in reverse + * order, but processes filters straight. This reverses + * the order of filters so as to produce the same final + * contained_items ordering as the normal ui way. */ + vector_insert_at(job->job_items, 0, items[i]); if (items[i]->item_type == item_type::BOULDER) rough = true; diff --git a/library/xml b/library/xml index 92dbeb626..b0d8e71dc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 92dbeb6267a24a35babe3d5326801f2e8cfd22e1 +Subproject commit b0d8e71dc9f6e697409bd999801097acdc57fcd8 From 2c0024adc91491b6d687f3b56d6a5779a3391e59 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 19:56:55 +0400 Subject: [PATCH 19/27] Make Items::getPosition exactly match the DF original in behavior. --- LUA_API.rst | 4 ++-- Lua API.html | 4 ++-- library/modules/Items.cpp | 34 +++++++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index bd2d33cb0..9ecd2e55e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -693,7 +693,7 @@ Units module * ``dfhack.units.getPosition(unit)`` - Returns true *x,y,z* of the unit; may be not equal to unit.pos if caged. + Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. * ``dfhack.units.getContainer(unit)`` @@ -752,7 +752,7 @@ Items module * ``dfhack.items.getPosition(item)`` - Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory. + Returns true *x,y,z* of the item, or *nil* if invalid; may be not equal to item.pos if in inventory. * ``dfhack.items.getGeneralRef(item, type)`` diff --git a/Lua API.html b/Lua API.html index d44d82bab..7ce777c42 100644 --- a/Lua API.html +++ b/Lua API.html @@ -935,7 +935,7 @@ a lua list containing them.

    Units module

    • dfhack.units.getPosition(unit)

      -

      Returns true x,y,z of the unit; may be not equal to unit.pos if caged.

      +

      Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.

    • dfhack.units.getContainer(unit)

      Returns the container (cage) item or nil.

      @@ -982,7 +982,7 @@ or raws. The ignore_noble boolean disables the

      Items module

      • dfhack.items.getPosition(item)

        -

        Returns true x,y,z of the item; may be not equal to item.pos if in inventory.

        +

        Returns true x,y,z of the item, or nil if invalid; may be not equal to item.pos if in inventory.

      • dfhack.items.getGeneralRef(item, type)

        Searches for a general_ref with the given type.

        diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index f1d495bf1..335d7d8fc 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -69,6 +69,7 @@ using namespace std; #include "df/general_ref_unit_itemownerst.h" #include "df/general_ref_contains_itemst.h" #include "df/general_ref_contained_in_itemst.h" +#include "df/vermin.h" using namespace DFHack; using namespace df::enums; @@ -512,9 +513,12 @@ df::coord Items::getPosition(df::item *item) { CHECK_NULL_POINTER(item); - if (item->flags.bits.in_inventory || - item->flags.bits.in_chest || - item->flags.bits.in_building) + /* Function reverse-engineered from DF code. */ + + if (item->flags.bits.removed) + return df::coord(); + + if (item->flags.bits.in_inventory) { for (size_t i = 0; i < item->itemrefs.size(); i++) { @@ -532,15 +536,31 @@ df::coord Items::getPosition(df::item *item) return Units::getPosition(unit); break; - case general_ref_type::BUILDING_HOLDER: + /*case general_ref_type::BUILDING_HOLDER: if (auto bld = ref->getBuilding()) return df::coord(bld->centerx, bld->centery, bld->z); + break;*/ + + default: break; + } + } + + for (size_t i = 0; i < item->specific_refs.size(); i++) + { + df::specific_ref *ref = item->specific_refs[i]; + + switch (ref->type) + { + case specific_ref_type::VERMIN_ESCAPED_PET: + return ref->vermin->pos; default: break; } } + + return df::coord(); } return item->pos; @@ -625,6 +645,10 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(container); + auto cpos = getPosition(container); + if (!cpos.isValid()) + return false; + if (!detachItem(mc, item)) return false; @@ -635,7 +659,7 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: { delete ref1; delete ref2; Core::printerr("Could not allocate container refs.\n"); - putOnGround(mc, item, getPosition(container)); + putOnGround(mc, item, cpos); return false; } From e9ef9b87b575b4746e2090556c192237c95f508a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:04:09 +0400 Subject: [PATCH 20/27] Add central locations for onUpdate and onStateChange handling in core. --- library/Core.cpp | 35 ++++++++++++++++++++++----------- library/PluginManager.cpp | 2 -- library/include/Core.h | 14 +++++++++++++ library/include/PluginManager.h | 10 ---------- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 7f08f402f..1d2a76026 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1017,7 +1017,7 @@ int Core::Update() Lua::Core::Reset(out, "DF code execution"); if (first_update) - plug_mgr->OnStateChange(out, SC_CORE_INITIALIZED); + onStateChange(out, SC_CORE_INITIALIZED); // detect if the game was loaded or unloaded in the meantime void *new_wdata = NULL; @@ -1043,11 +1043,11 @@ int Core::Update() // and if the world is going away, we report the map change first if(had_map) - plug_mgr->OnStateChange(out, SC_MAP_UNLOADED); + onStateChange(out, SC_MAP_UNLOADED); // and if the world is appearing, we report map change after that - plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); + onStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); if(isMapLoaded()) - plug_mgr->OnStateChange(out, SC_MAP_LOADED); + onStateChange(out, SC_MAP_LOADED); } // otherwise just check for map change... else if (new_mapdata != last_local_map_ptr) @@ -1058,7 +1058,7 @@ int Core::Update() if (isMapLoaded() != had_map) { getWorld()->ClearPersistentCache(); - plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); + onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); } } @@ -1071,15 +1071,12 @@ int Core::Update() if (screen != top_viewscreen) { top_viewscreen = screen; - plug_mgr->OnStateChange(out, SC_VIEWSCREEN_CHANGED); + onStateChange(out, SC_VIEWSCREEN_CHANGED); } } - // notify all the plugins that a game tick is finished - plug_mgr->OnUpdate(out); - - // process timers in lua - Lua::Core::onUpdate(out); + // Execute per-frame handlers + onUpdate(out); // Release the fake suspend lock { @@ -1116,6 +1113,22 @@ int Core::Update() return 0; }; +void Core::onUpdate(color_ostream &out) +{ + // notify all the plugins that a game tick is finished + plug_mgr->OnUpdate(out); + + // process timers in lua + Lua::Core::onUpdate(out); +} + +void Core::onStateChange(color_ostream &out, state_change_event event) +{ + plug_mgr->OnStateChange(out, event); + + Lua::Core::onStateChange(out, event); +} + // FIXME: needs to terminate the IO threads and properly dismantle all the machinery involved. int Core::Shutdown ( void ) { diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 320e8dec8..ae8cc755f 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -609,8 +609,6 @@ void PluginManager::OnStateChange(color_ostream &out, state_change_event event) { all_plugins[i]->on_state_change(out, event); } - - Lua::Core::onStateChange(out, event); } // FIXME: doesn't check name collisions! diff --git a/library/include/Core.h b/library/include/Core.h index fe83715cf..7da633cca 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -68,6 +68,17 @@ namespace DFHack class df_window; } + enum state_change_event + { + SC_WORLD_LOADED = 0, + SC_WORLD_UNLOADED = 1, + SC_MAP_LOADED = 2, + SC_MAP_UNLOADED = 3, + SC_VIEWSCREEN_CHANGED = 4, + SC_CORE_INITIALIZED = 5, + SC_BEGIN_UNLOAD = 6 + }; + // Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF. // There should never be more than one instance // Better than tracking some weird variables all over the place. @@ -162,6 +173,9 @@ namespace DFHack int SDL_Event(SDL::Event* event); bool ncurses_wgetch(int in, int & out); + void onUpdate(color_ostream &out); + void onStateChange(color_ostream &out, state_change_event event); + Core(Core const&); // Don't Implement void operator=(Core const&); // Don't implement diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 7f98bf422..02974f36b 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -65,16 +65,6 @@ namespace DFHack // Close a plugin library void ClosePlugin (DFLibrary * plugin); - enum state_change_event - { - SC_WORLD_LOADED = 0, - SC_WORLD_UNLOADED = 1, - SC_MAP_LOADED = 2, - SC_MAP_UNLOADED = 3, - SC_VIEWSCREEN_CHANGED = 4, - SC_CORE_INITIALIZED = 5, - SC_BEGIN_UNLOAD = 6 - }; struct DFHACK_EXPORT CommandReg { const char *name; int (*command)(lua_State*); From f37f708b37bf21ded2ca99fac6db3facd7a05fcf Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:38:27 +0400 Subject: [PATCH 21/27] Add a workaround for Planepacked bug in buildings constructed via API. Buildings hanging in the air cause constructWithFilters to exhibit the same behavior as a moody dwarf in a burrow excluding the workshop, i.e. endlessly collecting the same type of reagent. http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 The workaround monitors jobs and reclassifies the reagents on the fly. --- library/Core.cpp | 13 +++++++++ library/modules/Buildings.cpp | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/library/Core.cpp b/library/Core.cpp index 1d2a76026..e8c43b7f5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -71,6 +71,7 @@ using namespace DFHack; using namespace tthread; using namespace df::enums; using df::global::init; +using df::global::world; // FIXME: A lot of code in one file, all doing different things... there's something fishy about it. @@ -1113,8 +1114,18 @@ int Core::Update() return 0; }; +extern bool buildings_do_onupdate; +void buildings_onStateChange(color_ostream &out, state_change_event event); +void buildings_onUpdate(color_ostream &out); + +static int buildings_timer = 0; + void Core::onUpdate(color_ostream &out) { + // convert building reagents + if (buildings_do_onupdate && (++buildings_timer & 1)) + buildings_onUpdate(out); + // notify all the plugins that a game tick is finished plug_mgr->OnUpdate(out); @@ -1124,6 +1135,8 @@ void Core::onUpdate(color_ostream &out) void Core::onStateChange(color_ostream &out, state_change_event event) { + buildings_onStateChange(out, event); + plug_mgr->OnStateChange(out, event); Lua::Core::onStateChange(out, event); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index d2b3e3527..e0afc56ed 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -87,6 +87,58 @@ static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) return &extent.extents[dx + dy*extent.width]; } +/* + * A monitor to work around this bug, in its application to buildings: + * + * http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 + */ +bool buildings_do_onupdate = false; + +void buildings_onStateChange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_MAP_LOADED: + buildings_do_onupdate = true; + break; + case SC_MAP_UNLOADED: + buildings_do_onupdate = false; + break; + default: + break; + } +} + +void buildings_onUpdate(color_ostream &out) +{ + buildings_do_onupdate = false; + + df::job_list_link *link = world->job_list.next; + for (; link; link = link->next) { + df::job *job = link->item; + + if (job->job_type != job_type::ConstructBuilding) + continue; + if (job->job_items.empty()) + continue; + + buildings_do_onupdate = true; + + for (size_t i = 0; i < job->items.size(); i++) + { + df::job_item_ref *iref = job->items[i]; + if (iref->role != df::job_item_ref::Reagent) + continue; + df::job_item *item = vector_get(job->job_items, iref->job_item_idx); + if (!item) + continue; + // Convert Reagent to Hauled, while decrementing quantity + item->quantity = std::max(0, item->quantity-1); + iref->role = df::job_item_ref::Hauled; + iref->job_item_idx = -1; + } + } +} + uint32_t Buildings::getNumBuildings() { return world->buildings.all.size(); @@ -906,6 +958,8 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectormat_index = items[i]->mat_index; } + buildings_do_onupdate = true; + createDesign(bld, rough); return true; } @@ -969,3 +1023,4 @@ bool Buildings::deconstruct(df::building *bld) return true; } + From 52426f9035c54fdfcd38992259c94a65de160ed8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:41:41 +0400 Subject: [PATCH 22/27] Track xml --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index b0d8e71dc..ab102b93f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b0d8e71dc9f6e697409bd999801097acdc57fcd8 +Subproject commit ab102b93fca9b1ae8b5fbfc3ae5a9ee06ee60811 From 3f542b4df10d8ff7df6151ee4eee2707134c696c Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 18 May 2012 07:40:05 -0500 Subject: [PATCH 23/27] Update merge --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index ab102b93f..3c8269225 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ab102b93fca9b1ae8b5fbfc3ae5a9ee06ee60811 +Subproject commit 3c8269225b1a68fd8803b7b7160d547247449789 From 48185568e9a9370f843fb28f1ff53335639b92d6 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 18 May 2012 16:05:29 +0300 Subject: [PATCH 24/27] Added df::Items::moveToBuilding. Moved item from ground to building, usefull for museum forts (placing items on tables) --- library/LuaApi.cpp | 7 +++++++ library/include/modules/Items.h | 4 +++- library/modules/Items.cpp | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 529bdd077..d4c3b157d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -755,6 +755,12 @@ static bool items_moveToContainer(df::item *item, df::item *container) return Items::moveToContainer(mc, item, container); } +static bool items_moveToBuilding(df::item *item, df::building_actual *building, int use_mode) +{ + MapExtras::MapCache mc; + return Items::moveToBuilding(mc, item, building,use_mode); +} + static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, getGeneralRef), WRAPM(Items, getSpecificRef), @@ -763,6 +769,7 @@ static const LuaWrapper::FunctionReg dfhack_items_module[] = { WRAPM(Items, getContainer), WRAPN(moveToGround, items_moveToGround), WRAPN(moveToContainer, items_moveToContainer), + WRAPN(moveToBuilding, items_moveToBuilding), { NULL, NULL } }; diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 8353d24c3..54db8e6ca 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -37,6 +37,7 @@ distribution. #include "df/item_type.h" #include "df/general_ref.h" #include "df/specific_ref.h" +#include "df/building_actual.h" namespace df { @@ -147,5 +148,6 @@ DFHACK_EXPORT df::coord getPosition(df::item *item); DFHACK_EXPORT bool moveToGround(MapExtras::MapCache &mc, df::item *item, df::coord pos); DFHACK_EXPORT bool moveToContainer(MapExtras::MapCache &mc, df::item *item, df::item *container); +DFHACK_EXPORT bool moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode); +} } -} \ No newline at end of file diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 335d7d8fc..a18ba60cb 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -48,6 +48,7 @@ using namespace std; #include "df/world.h" #include "df/item.h" #include "df/building.h" +#include "df/building_actual.h" #include "df/tool_uses.h" #include "df/itemdef_weaponst.h" #include "df/itemdef_trapcompst.h" @@ -69,6 +70,7 @@ using namespace std; #include "df/general_ref_unit_itemownerst.h" #include "df/general_ref_contains_itemst.h" #include "df/general_ref_contained_in_itemst.h" +#include "df/general_ref_building_holderst.h" #include "df/vermin.h" using namespace DFHack; @@ -676,3 +678,34 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: return true; } +DFHACK_EXPORT bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df::building_actual *building,int16_t use_mode) +{ + CHECK_NULL_POINTER(item); + CHECK_NULL_POINTER(building); + + auto ref = df::allocate(); + if(!ref) + { + delete ref; + Core::printerr("Could not allocate building holder refs.\n"); + return false; + } + if (!detachItem(mc, item)) + { + delete ref; + return false; + } + item->pos.x=building->centerx; + item->pos.y=building->centery; + item->pos.z=building->z; + item->flags.bits.in_building=true; + + ref->building_id=building->id; + item->itemrefs.push_back(ref); + + auto con=new df::building_actual::T_contained_items; + con->item=item; + con->use_mode=use_mode; + building->contained_items.push_back(con); + return true; +} From 51ad697bf504754890f75f0aa4c527db546c7767 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 18 May 2012 08:25:25 -0500 Subject: [PATCH 25/27] Fix unresolved symbol reference in Windows platform. --- library/PlugLoad-windows.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/library/PlugLoad-windows.cpp b/library/PlugLoad-windows.cpp index f9a75df97..714960e64 100644 --- a/library/PlugLoad-windows.cpp +++ b/library/PlugLoad-windows.cpp @@ -28,6 +28,7 @@ distribution. #include #include #include +#include "Core.h" #include "PluginManager.h" #include "Hooks.h" #include From 3c44e22760b6cead3b42194097c24700b314683a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 18 May 2012 17:54:05 +0400 Subject: [PATCH 26/27] Support trade viewscreens in Items::getSelectedItem. --- library/include/PluginManager.h | 2 ++ library/modules/Gui.cpp | 30 ++++++++++++++++++++++++++++++ library/xml | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 02974f36b..31633dfe2 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -31,6 +31,8 @@ distribution. #include #include +#include "Core.h" + #include "RemoteClient.h" typedef struct lua_State lua_State; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 2d9402fca..461921391 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -54,7 +54,9 @@ using namespace DFHack; #include "df/viewscreen_layer_workshop_profilest.h" #include "df/viewscreen_layer_noblelistst.h" #include "df/viewscreen_layer_overall_healthst.h" +#include "df/viewscreen_layer_assigntradest.h" #include "df/viewscreen_petst.h" +#include "df/viewscreen_tradegoodsst.h" #include "df/ui_unit_view_mode.h" #include "df/ui_sidebar_menus.h" #include "df/ui_look_list.h" @@ -69,6 +71,7 @@ using namespace DFHack; #include "df/interfacest.h" #include "df/graphic.h" #include "df/layer_object_listst.h" +#include "df/assign_trade_status.h" using namespace df::enums; using df::global::gview; @@ -420,6 +423,33 @@ static df::item *getAnyItem(df::viewscreen *top) return ref ? ref->getItem() : NULL; } + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_assigntradest, top)) + { + auto list1 = getLayerList(screen, 0); + auto list2 = getLayerList(screen, 1); + if (!list1 || !list2 || !list2->bright) + return NULL; + + int list_idx = vector_get(screen->visible_lists, list1->cursor, (int16_t)-1); + unsigned num_lists = sizeof(screen->lists)/sizeof(std::vector); + if (unsigned(list_idx) >= num_lists) + return NULL; + + int idx = vector_get(screen->lists[list_idx], list2->cursor, -1); + if (auto info = vector_get(screen->info, idx)) + return info->item; + + return NULL; + } + + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_tradegoodsst, top)) + { + if (screen->in_right_pane) + return vector_get(screen->broker_items, screen->broker_cursor); + else + return vector_get(screen->trader_items, screen->trader_cursor); + } + if (!Gui::dwarfmode_hotkey(top)) return NULL; diff --git a/library/xml b/library/xml index 3c8269225..f004804fd 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 3c8269225b1a68fd8803b7b7160d547247449789 +Subproject commit f004804fd655efcb4bf9ea31bd95ae4f759937b4 From e2f39368b17bbc9d6b380faed7ceb52b1bd8eb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 18 May 2012 17:43:22 +0200 Subject: [PATCH 27/27] Sync submodules --- plugins/stonesense | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/stonesense b/plugins/stonesense index 15e3b726c..37a823541 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 15e3b726c3e68d2985aecd95d2a33bf4550caaa1 +Subproject commit 37a823541538023b9f3d0d1e8039cf32851de68d