From 27bdc9f2df02ccd566eddc5fe1ffa2a09d7e6143 Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 23 Aug 2012 21:38:38 +0300 Subject: [PATCH 01/79] Start gutting dfusion. --- plugins/Dfusion/readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/Dfusion/readme.txt b/plugins/Dfusion/readme.txt index c40ac3707..df3b85b1f 100644 --- a/plugins/Dfusion/readme.txt +++ b/plugins/Dfusion/readme.txt @@ -10,3 +10,5 @@ Similar to dfusion but not interactive. To be used with hotkeys (later will have Also dfuse/dfusion runs an init script located at 'save directory/dfusion/init.lua'. And 'initcustom.lua' if it exists More info http://dwarffortresswiki.org/index.php/Utility:DFusion + +a \ No newline at end of file From 90021b4e5e8451e1505f1344b4862a05cbe9711a Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 30 Aug 2012 20:41:10 +0300 Subject: [PATCH 02/79] simple_embark/plugin.lua sanitized --- plugins/Dfusion/luafiles/init.lua | 11 +++-------- .../Dfusion/luafiles/simple_embark/plugin.lua | 16 ++++++++++++---- plugins/Dfusion/readme.txt | 2 -- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index 19f63d603..c8eebcd23 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -1,11 +1,7 @@ -function err(msg) --make local maybe... - print(msg) - print(debug.traceback()) -end function dofile(filename) --safer dofile, with traceback (very usefull) f,perr=loadfile(filename) if f~=nil then - return xpcall(f,err) + return safecall(f) else print(perr) end @@ -13,7 +9,7 @@ end function dofile_silent(filename) --safer dofile, with traceback, no file not found error f,perr=loadfile(filename) if f~=nil then - return xpcall(f,err) + return safecall(f) else if(string.sub(perr,1,11)~="cannot open") then --ugly hack print(perr) @@ -26,7 +22,6 @@ function loadall(t1) --loads all non interactive plugin parts, so that later the end end function mainmenu(t1) - while true do print("No. Name Desc") for k,v in pairs(t1) do @@ -58,7 +53,7 @@ dofile("dfusion/common.lua") dofile("dfusion/utils.lua") dofile("dfusion/offsets_misc.lua") dofile("dfusion/editor.lua") ---dofile("dfusion/xml_struct.lua") + unlockDF() plugins={} table.insert(plugins,{"simple_embark","A simple embark dwarf count editor"}) diff --git a/plugins/Dfusion/luafiles/simple_embark/plugin.lua b/plugins/Dfusion/luafiles/simple_embark/plugin.lua index c64aa7e68..f4f594e8d 100644 --- a/plugins/Dfusion/luafiles/simple_embark/plugin.lua +++ b/plugins/Dfusion/luafiles/simple_embark/plugin.lua @@ -1,10 +1,18 @@ function simple_embark(num) -stoff=VersionInfo.getAddress('start_dwarf_count') -print("Starting dwarves found:"..engine.peekd(stoff)) -engine.poked(stoff,num) + local stoff=dfhack.internal.getAddress('start_dwarf_count') + print("Starting dwarves found:"..df.reinterpret_cast('int32_t', stoff).value) + local tmp_val=df.new('int32_t') + local size,pos=tmp_val:sizeof() + tmp_val.value=num + local ret=dfhack.internal.patchMemory(stoff,tmp_val,size) + if ret then + print("Success!") + else + qerror("Failed to patch in number") + end end if not(FILE) then -print("Type in new ammount:") +print("Type in new ammount (more than 6, less than 15000):") repeat ans=tonumber(io.read()) if ans==nil or not(ans<=15000 and ans>0) then diff --git a/plugins/Dfusion/readme.txt b/plugins/Dfusion/readme.txt index df3b85b1f..c40ac3707 100644 --- a/plugins/Dfusion/readme.txt +++ b/plugins/Dfusion/readme.txt @@ -10,5 +10,3 @@ Similar to dfusion but not interactive. To be used with hotkeys (later will have Also dfuse/dfusion runs an init script located at 'save directory/dfusion/init.lua'. And 'initcustom.lua' if it exists More info http://dwarffortresswiki.org/index.php/Utility:DFusion - -a \ No newline at end of file From f8744e2ec259cdb72c77e6095cf2e7e329ba081d Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 31 Aug 2012 23:46:33 +0300 Subject: [PATCH 03/79] Experimental stuff editor (can and will crash DF ) --- scripts/gui/gm-Items.lua | 181 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 scripts/gui/gm-Items.lua diff --git a/scripts/gui/gm-Items.lua b/scripts/gui/gm-Items.lua new file mode 100644 index 000000000..82a1da154 --- /dev/null +++ b/scripts/gui/gm-Items.lua @@ -0,0 +1,181 @@ +-- Interface powered item editor. +-- TODO use this: MechanismList = defclass(MechanismList, guidm.MenuOverlay) +local gui = require 'gui' + +if dfhack.gui.getCurFocus() ~= 'item' then + qerror("This script requires the item view.") +end + +TextInputDialog = defclass(TextInputDialog, gui.FramedScreen) + +function TextInputDialog:init(prompt) + self.frame_style=GREY_LINE_FRAME + self.frame_title=prompt + self.input="" + return self +end +function TextInputDialog:onRenderBody(dc) + dc:seek(1,1):string(self.input, COLOR_WHITE):newline() +end + +local MODE_BROWSE=0 +local MODE_EDIT=1 +local item_screen={ + frame_style = gui.GREY_LINE_FRAME, + frame_title = "GameMaster's editor", + stack={}, + item_count=0, + mode=MODE_BROWSE, + + keys={}, + insertNew=function(self) + --[=[local trg=self:currentTarget() -- not sure if possible... + if trg.target and trg.target._kind and trg.target._kind=="container" then + local thing=df.new('general_ref_contained_itemst') + trg.target:insert('#',trg.keys[trg.selected]) + end]=] + end, + deleteSelected=function(self) + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="container" then + trg.target:erase(trg.keys[trg.selected]) + end + end, + currentTarget=function(self) + return self.stack[#self.stack] + end, + changeSelected = function (self,delta) + local trg=self:currentTarget() + if trg.item_count <= 1 then return end + trg.selected = 1 + (trg.selected + delta - 1) % trg.item_count + end, + editSelected = function(self) + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="bitfield" then + trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] + else + --print(type(trg.target[trg.keys[trg.selected]]),trg.target[trg.keys[trg.selected]]._kind or "") + local trg_type=type(trg.target[trg.keys[trg.selected]]) + if trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected + self.mode=MODE_EDIT + self.input=tostring(trg.target[trg.keys[trg.selected]]) + elseif trg_type=='userdata' then + self:pushTarget(trg.target[trg.keys[trg.selected]]) + --local screen = mkinstance(gui.FramedScreen,item_screen):init(trg.target[trg.keys[trg.selected]]) -- does not work + --screen:show() + else + print("Unknow type:"..trg_type) + print("Subtype:"..tostring(trg.target[trg.keys[trg.selected]]._kind)) + end + end + end, + cancelEdit = function(self) + self.mode=MODE_BROWSE + self.input="" + end, + commitEdit = function(self) + local trg=self:currentTarget() + self.mode=MODE_BROWSE + if type(trg.target[trg.keys[trg.selected]])=='number' then + trg.target[trg.keys[trg.selected]]=tonumber(self.input) + elseif type(trg.target[trg.keys[trg.selected]])=='string' then + trg.target[trg.keys[trg.selected]]=self.input + end + end, + onRenderBody = function(self, dc) + local trg=self:currentTarget() + dc:seek(2,1):string(tostring(trg.target), COLOR_RED) + local offset=2 + local page_offset=0 + local current_item=1 + local t_col + if math.floor(trg.selected / (self.frame_height-offset-2)) >0 then + page_offset=math.floor(trg.selected / (self.frame_height-offset-2))*(self.frame_height-offset-2)-1 + end + for k,v in pairs(trg.target) do + + if current_item==trg.selected then + t_col=COLOR_LIGHTGREEN + else + t_col=COLOR_GRAY + end + + if current_item-page_offset > 0 then + local y_pos=current_item-page_offset+offset + dc:seek(2,y_pos):string(tostring(k),t_col) + + if self.mode==MODE_EDIT and current_item==trg.selected then + dc:seek(20,y_pos):string(self.input..'_',COLOR_GREEN) + else + dc:seek(20,y_pos):string(tostring(v),t_col) + end + end + current_item=current_item+1 + end + end, + + onInput = function(self,keys) + if self.mode==MODE_BROWSE then + if keys.LEAVESCREEN then + self:popTarget() + elseif keys.CURSOR_UP then + self:changeSelected(-1) + elseif keys.CURSOR_DOWN then + self:changeSelected(1) + elseif keys.CURSOR_UP_FAST then + self:changeSelected(-10) + elseif keys.CURSOR_DOWN_FAST then + self:changeSelected(10) + elseif keys.SELECT then + self:editSelected() + elseif keys.CUSTOM_ALT_E then + --self:specialEditor() + local screen = mkinstance(TextInputDialog):init("Input new coordinates") + screen:show() + elseif keys.CUSTOM_ALT_I then --insert + self:insertNew() + elseif keys.CUSTOM_ALT_D then --delete + self:deleteSelected() + end + elseif self.mode==MODE_EDIT then + if keys.LEAVESCREEN then + self:cancelEdit() + elseif keys.SELECT then + self:commitEdit() + elseif keys._STRING then + if keys._STRING==0 then + self.input=string.sub(self.input,1,-2) + else + self.input=self.input.. string.char(keys._STRING) + end + end + end + end, + pushTarget=function(self,target_to_push) + local new_tbl={} + new_tbl.target=target_to_push + new_tbl.keys={} + new_tbl.selected=1 + for k,v in pairs(target_to_push) do + table.insert(new_tbl.keys,k) + end + new_tbl.item_count=#new_tbl.keys + table.insert(self.stack,new_tbl) + end, + popTarget=function(self) + table.remove(self.stack) --removes last element + if #self.stack==0 then + self:dismiss() + end + end, + init = function(self,item_to_edit) + self:pushTarget(item_to_edit) + self.frame_width,self.frame_height=dfhack.screen.getWindowSize() + return self + end + } + + + +local screen = mkinstance(gui.FramedScreen,item_screen):init(dfhack.gui.getCurViewscreen().item) +screen:show() \ No newline at end of file From af155db3beca208cf1f842246db342db09bdc5cc Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 01:22:51 +0300 Subject: [PATCH 04/79] Added whole bunch of editable things (units, jobs, flows) --- scripts/gui/gm-Items.lua | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/gui/gm-Items.lua b/scripts/gui/gm-Items.lua index 82a1da154..839d3158a 100644 --- a/scripts/gui/gm-Items.lua +++ b/scripts/gui/gm-Items.lua @@ -2,8 +2,27 @@ -- TODO use this: MechanismList = defclass(MechanismList, guidm.MenuOverlay) local gui = require 'gui' -if dfhack.gui.getCurFocus() ~= 'item' then - qerror("This script requires the item view.") +local my_trg +if dfhack.gui.getCurFocus() == 'item' then + my_trg=dfhack.gui.getCurViewscreen().item +elseif dfhack.gui.getCurFocus() == 'joblist' then + local t_screen=dfhack.gui.getCurViewscreen() + my_trg=t_screen.jobs[t_screen.cursor_pos] +elseif dfhack.gui.getCurFocus() == 'createquota' then + local t_screen=dfhack.gui.getCurViewscreen() + my_trg=t_screen.orders[t_screen.sel_idx] +elseif dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Flow' then + local t_look=df.global.ui_look_list.items[df.global.ui_look_cursor] + my_trg=t_look.flow + +elseif dfhack.gui.getSelectedUnit(true) then + my_trg=dfhack.gui.getSelectedUnit(true) +elseif dfhack.gui.getSelectedItem(true) then + my_trg=dfhack.gui.getSelectedItem(true) +elseif dfhack.gui.getSelectedJob(true) then + my_trg=dfhack.gui.getSelectedJob(true) +else + qerror("No valid target found") end TextInputDialog = defclass(TextInputDialog, gui.FramedScreen) @@ -177,5 +196,5 @@ local item_screen={ -local screen = mkinstance(gui.FramedScreen,item_screen):init(dfhack.gui.getCurViewscreen().item) +local screen = mkinstance(gui.FramedScreen,item_screen):init(my_trg) screen:show() \ No newline at end of file From c9c587af9aeae439313f64b23d40a862672726e2 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 01:27:01 +0300 Subject: [PATCH 05/79] small fix for boolean values --- scripts/gui/gm-Items.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/gui/gm-Items.lua b/scripts/gui/gm-Items.lua index 839d3158a..34e744ee2 100644 --- a/scripts/gui/gm-Items.lua +++ b/scripts/gui/gm-Items.lua @@ -78,6 +78,8 @@ local item_screen={ if trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected self.mode=MODE_EDIT self.input=tostring(trg.target[trg.keys[trg.selected]]) + elseif trg_type=='boolean' then + trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] elseif trg_type=='userdata' then self:pushTarget(trg.target[trg.keys[trg.selected]]) --local screen = mkinstance(gui.FramedScreen,item_screen):init(trg.target[trg.keys[trg.selected]]) -- does not work From d784d4bc40e2b0b0d91513a22a3c22f5e042d59c Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 10:05:31 +0300 Subject: [PATCH 06/79] Static code segment search for memscan.lua --- library/lua/memscan.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index 970f821c2..521f7c7e8 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -195,6 +195,36 @@ function MemoryArea:delete() for k,v in pairs(self) do self[k] = nil end end +-- Static code segment search +local function find_code_segment() + local code_start, code_end + + for i,mem in ipairs(dfhack.internal.getMemRanges()) do + if code_end then + if mem.start_addr == code_end and mem.read and not mem.write then + code_end = mem.end_addr + else + break + end + elseif mem.read and not mem.write + and (string.match(mem.name,'/dwarfort%.exe$') + or string.match(mem.name,'/Dwarf_Fortress$') + or string.match(mem.name,'Dwarf Fortress%.exe')) + then + code_start = mem.start_addr + code_end = mem.end_addr + end + end + + return code_start,code_end +end + +function get_code_segment() + local s, e = find_code_segment() + if s and e then + return ms.MemoryArea.new(s, e) + end +end -- Static data segment search local function find_data_segment() From 7cabf1b8435779c74ed498745920b8edbbacdd15 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 10:13:08 +0300 Subject: [PATCH 07/79] Small bug fix --- library/lua/memscan.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index 521f7c7e8..af2fe7435 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -222,7 +222,7 @@ end function get_code_segment() local s, e = find_code_segment() if s and e then - return ms.MemoryArea.new(s, e) + return MemoryArea.new(s, e) end end -- Static data segment search From 532839a4d5fd37fdbd52edd9cf5f7661f8232bdb Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 10:54:45 +0300 Subject: [PATCH 08/79] Embark anywhere ported --- library/lua/memscan.lua | 13 +++++++++++++ plugins/Dfusion/luafiles/simple_embark/plugin.lua | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index af2fe7435..85ed5624a 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -225,6 +225,19 @@ function get_code_segment() return MemoryArea.new(s, e) end end +function get_code_segments() + local ret={} + for i,mem in ipairs(dfhack.internal.getMemRanges()) do + if mem.read and not mem.write + and (string.match(mem.name,'/dwarfort%.exe$') + or string.match(mem.name,'/Dwarf_Fortress$') + or string.match(mem.name,'Dwarf Fortress%.exe')) + then + table.insert(ret,MemoryArea.new(mem.start_addr,mem.end_addr)) + end + end + return ret +end -- Static data segment search local function find_data_segment() diff --git a/plugins/Dfusion/luafiles/simple_embark/plugin.lua b/plugins/Dfusion/luafiles/simple_embark/plugin.lua index f4f594e8d..abc3530c2 100644 --- a/plugins/Dfusion/luafiles/simple_embark/plugin.lua +++ b/plugins/Dfusion/luafiles/simple_embark/plugin.lua @@ -12,7 +12,7 @@ function simple_embark(num) end end if not(FILE) then -print("Type in new ammount (more than 6, less than 15000):") +print("Type in new amount (more than 6, less than 15000):") repeat ans=tonumber(io.read()) if ans==nil or not(ans<=15000 and ans>0) then From 5b60dc296a1e039c0fb0084f63f07a3825ab0ad8 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 21:53:52 +0300 Subject: [PATCH 09/79] Renamed editor and added example keybinding --- dfhack.init-example | 3 +++ scripts/gui/{gm-Items.lua => gm-editor.lua} | 0 2 files changed, 3 insertions(+) rename scripts/gui/{gm-Items.lua => gm-editor.lua} (100%) diff --git a/dfhack.init-example b/dfhack.init-example index d3a28b9b0..a9de146c2 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -52,6 +52,9 @@ keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work # interface for the liquids plugin keybinding add Alt-L@dwarfmode/LookAround gui/liquids +# interface for universal game master's editor +keybinding add Alt-Shift-E gui/gm-editor + ################### # UI logic tweaks # ################### diff --git a/scripts/gui/gm-Items.lua b/scripts/gui/gm-editor.lua similarity index 100% rename from scripts/gui/gm-Items.lua rename to scripts/gui/gm-editor.lua From 2574bb1e3d277627f1855ec78e59c4a4ace5e7dd Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 1 Sep 2012 21:58:01 +0300 Subject: [PATCH 10/79] embark anywhere upgrade. --- plugins/Dfusion/luafiles/tools/init.lua | 32 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index e3a4607cf..b9978d020 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -1,3 +1,4 @@ +local ms=require "memscan" tools={} tools.menu=MakeMenu() function tools.setrace(name) @@ -60,14 +61,31 @@ function tools.GiveSentience(names) end end tools.menu:add("Give Sentience",tools.GiveSentience) -function tools.embark() - off=offsets.find(0,0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04) - if off~=0 then - engine.pokeb(off+5,0x90) - engine.pokeb(off+6,0x90) - print("Found and patched") +function embark() --windows only? + local seg=ms.get_code_segments() + printall(seg) + local idx,off + for k,v in ipairs(seg) do + idx,off=v.uint8_t:find_one{0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04} + if idx then + break + end + end + + if idx then + local tmp_val=df.new('uint8_t',2) + tmp_val[0]=0x90 + tmp_val[1]=0x90 + local size,pos=tmp_val:sizeof() + local ret=dfhack.internal.patchMemory(off+5,pos,size*2) + if ret then + print("Found and patched:",off+5) + else + print("Patching failed at:",off+5) + end + tmp_val:delete() else - print("not found") + qerror("Offset for embark patch not found!") end end tools.menu:add("Embark anywhere",tools.embark) From 6fc10fc2683e82dc6c0552507f1700214019a514 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 5 Sep 2012 21:52:54 +0300 Subject: [PATCH 11/79] Fixed embark anywhere to use more sane code segment search --- library/lua/memscan.lua | 41 ++++++------------------- plugins/Dfusion/luafiles/tools/init.lua | 12 ++------ 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index 85ed5624a..ba3efd708 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -196,48 +196,25 @@ function MemoryArea:delete() end -- Static code segment search -local function find_code_segment() - local code_start, code_end + +function get_code_segment() + local cstart, cend for i,mem in ipairs(dfhack.internal.getMemRanges()) do - if code_end then - if mem.start_addr == code_end and mem.read and not mem.write then - code_end = mem.end_addr - else - break - end - elseif mem.read and not mem.write + if mem.read and mem.execute and (string.match(mem.name,'/dwarfort%.exe$') or string.match(mem.name,'/Dwarf_Fortress$') or string.match(mem.name,'Dwarf Fortress%.exe')) then - code_start = mem.start_addr - code_end = mem.end_addr + cstart = mem.start_addr + cend = mem.end_addr end end - - return code_start,code_end -end - -function get_code_segment() - local s, e = find_code_segment() - if s and e then - return MemoryArea.new(s, e) + if cstart and cend then + return MemoryArea.new(cstart, cend) end end -function get_code_segments() - local ret={} - for i,mem in ipairs(dfhack.internal.getMemRanges()) do - if mem.read and not mem.write - and (string.match(mem.name,'/dwarfort%.exe$') - or string.match(mem.name,'/Dwarf_Fortress$') - or string.match(mem.name,'Dwarf Fortress%.exe')) - then - table.insert(ret,MemoryArea.new(mem.start_addr,mem.end_addr)) - end - end - return ret -end + -- Static data segment search local function find_data_segment() diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index b9978d020..7052715bc 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -61,17 +61,11 @@ function tools.GiveSentience(names) end end tools.menu:add("Give Sentience",tools.GiveSentience) -function embark() --windows only? - local seg=ms.get_code_segments() - printall(seg) +function tools.embark() --windows only? + local seg=ms.get_code_segment() local idx,off - for k,v in ipairs(seg) do - idx,off=v.uint8_t:find_one{0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04} - if idx then - break - end - end + idx,off=seg.uint8_t:find_one{0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04} if idx then local tmp_val=df.new('uint8_t',2) tmp_val[0]=0x90 From 85fc3384dd82f959be5fa0a8bedca995f674c67e Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 7 Sep 2012 17:25:39 +0300 Subject: [PATCH 12/79] Little cleanup and update to gm-editor --- .../luafiles/patterns/supplementary.xml | 7 --- .../luafiles/patterns/xml_angavrilov.lua | 55 ------------------- scripts/gui/gm-editor.lua | 24 ++++++-- 3 files changed, 19 insertions(+), 67 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/patterns/supplementary.xml delete mode 100644 plugins/Dfusion/luafiles/patterns/xml_angavrilov.lua diff --git a/plugins/Dfusion/luafiles/patterns/supplementary.xml b/plugins/Dfusion/luafiles/patterns/supplementary.xml deleted file mode 100644 index e341a1368..000000000 --- a/plugins/Dfusion/luafiles/patterns/supplementary.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/patterns/xml_angavrilov.lua b/plugins/Dfusion/luafiles/patterns/xml_angavrilov.lua deleted file mode 100644 index dbd3ad692..000000000 --- a/plugins/Dfusion/luafiles/patterns/xml_angavrilov.lua +++ /dev/null @@ -1,55 +0,0 @@ - -function parseargs(s) - local arg = {} - string.gsub(s, "([%w%-]+)=([\"'])(.-)%2", function (w, _, a) - arg[w] = a - end) - return arg -end - -function collect(s) - local stack = {} - local top = {} - table.insert(stack, top) - local ni,c,label,xarg, empty - local i, j = 1, 1 - while true do - ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:%-]+)(.-)(%/?)>", i) - if not ni then break end - local text = string.sub(s, i, ni-1) - if not string.find(text, "^%s*$") then - table.insert(top, text) - end - if empty == "/" then -- empty element tag - table.insert(top, {label=label, xarg=parseargs(xarg), empty=1}) - elseif c == "" then -- start tag - top = {label=label, xarg=parseargs(xarg)} - table.insert(stack, top) -- new level - else -- end tag - local toclose = table.remove(stack) -- remove top - top = stack[#stack] - if #stack < 1 then - error("nothing to close with "..label) - end - if toclose.label ~= label then - error("trying to close "..toclose.label.." with "..label) - end - table.insert(top, toclose) - end - i = j+1 - end - local text = string.sub(s, i) - if not string.find(text, "^%s*$") then - table.insert(stack[#stack], text) - end - if #stack > 1 then - error("unclosed "..stack[#stack].label) - end - return stack[1] -end - -function parseXmlFile(path) - local f, e = io.open(path, "r") - local xml = f:read("*a") - return collect(xml) -end \ No newline at end of file diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index 34e744ee2..d95cb652b 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -1,6 +1,7 @@ -- Interface powered item editor. -- TODO use this: MechanismList = defclass(MechanismList, guidm.MenuOverlay) local gui = require 'gui' +local dialog = require 'gui.dialogs' local my_trg if dfhack.gui.getCurFocus() == 'item' then @@ -47,12 +48,25 @@ local item_screen={ mode=MODE_BROWSE, keys={}, - insertNew=function(self) - --[=[local trg=self:currentTarget() -- not sure if possible... + + insertNew=function(self,typename) + local tp=typename + if typename== nil then + dialog.showInputPrompt("Class type","Input class type\n:",COLOR_WHITE,"",dfhack.curry(self.insertNew,self)) + return + end + local ntype=df[tp] + if ntype== nil then + dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED) + return + end + + local trg=self:currentTarget() if trg.target and trg.target._kind and trg.target._kind=="container" then - local thing=df.new('general_ref_contained_itemst') - trg.target:insert('#',trg.keys[trg.selected]) - end]=] + local thing=ntype:new() + dfhack.call_with_finalizer(1,false,df.delete,thing,trg.target.insert,trg.target,'#',thing) + + end end, deleteSelected=function(self) local trg=self:currentTarget() From 4f9732bfdae5e09ab0f7d3f2825e77e4c76c2dab Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 15 Sep 2012 15:44:15 +0300 Subject: [PATCH 13/79] Useless files removed, small bugfix --- plugins/Dfusion/luafiles/adv_tools/init.lua | 6 +- plugins/Dfusion/luafiles/buildingpatterns.lua | 24 - plugins/Dfusion/luafiles/editor.lua | 150 ---- plugins/Dfusion/luafiles/itempatterns.lua | 62 -- plugins/Dfusion/luafiles/patterns.lua | 248 ------ plugins/Dfusion/luafiles/patterns2.lua | 29 - plugins/Dfusion/luafiles/xml_struct.lua | 151 ---- plugins/Dfusion/luafiles/xml_types.lua | 734 ------------------ .../Dfusion/luafiles/xml_types_windows.lua | 159 ---- 9 files changed, 3 insertions(+), 1560 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/buildingpatterns.lua delete mode 100644 plugins/Dfusion/luafiles/editor.lua delete mode 100644 plugins/Dfusion/luafiles/itempatterns.lua delete mode 100644 plugins/Dfusion/luafiles/patterns.lua delete mode 100644 plugins/Dfusion/luafiles/patterns2.lua delete mode 100644 plugins/Dfusion/luafiles/xml_struct.lua delete mode 100644 plugins/Dfusion/luafiles/xml_types.lua delete mode 100644 plugins/Dfusion/luafiles/xml_types_windows.lua diff --git a/plugins/Dfusion/luafiles/adv_tools/init.lua b/plugins/Dfusion/luafiles/adv_tools/init.lua index 5504f32bc..566484f01 100644 --- a/plugins/Dfusion/luafiles/adv_tools/init.lua +++ b/plugins/Dfusion/luafiles/adv_tools/init.lua @@ -18,7 +18,7 @@ function adv_tools.reincarnate(swap_soul) --only for adventurer i guess for i=#events-1,0,-1 do -- reverse search because almost always it will be last entry if df.history_event_hist_figure_diedst:is_instance(events[i]) then --print("is instance:"..i) - if events[i].hfid==hist_fig.id then + if events[i].victim==hist_fig.id then --print("Is same id:"..i) trg_hist_fig=events[i].slayer if trg_hist_fig then @@ -29,12 +29,12 @@ function adv_tools.reincarnate(swap_soul) --only for adventurer i guess end end if trg_hist_fig ==nil then - error("Slayer not found") + qerror("Slayer not found") end local trg_unit=trg_hist_fig.unit_id if trg_unit==nil then - error("Unit id not found!") + qerror("Unit id not found!") end local trg_unit_final=df.unit.find(trg_unit) diff --git a/plugins/Dfusion/luafiles/buildingpatterns.lua b/plugins/Dfusion/luafiles/buildingpatterns.lua deleted file mode 100644 index 310c543cb..000000000 --- a/plugins/Dfusion/luafiles/buildingpatterns.lua +++ /dev/null @@ -1,24 +0,0 @@ -ptr_building={} -ptr_building.RTI={off=0,rtype=DWORD} -ptr_building.xs={off=4,rtype=DWORD} -ptr_building.ys={off=6,rtype=DWORD} -ptr_building.zs={off=8,rtype=DWORD} -ptr_building.xe={off=12,rtype=DWORD} -ptr_building.ye={off=16,rtype=DWORD} -ptr_building.ze={off=20,rtype=DWORD} -ptr_building.flags={off=24,rtype=ptt_dfflag.new(4)} -ptr_building.materials={off=28,rtype=DWORD} -ptr_building.builditems={off=228,rtype=ptr_vector} -function ptr_building.getname(self,RTI) - if RTI == nil then - return string.sub(RTTI_GetName(self.RTI),5,-3) - else - return string.sub(RTTI_GetName(RTI),5,-3) - end -end -ptr_subbuilding={} -ptr_subbuilding["building_trapst"]={} -ptr_subbuilding["building_trapst"].state={off=250,rtype=DWORD} -- atleast lever has this -ptr_subbuilding["building_doorst"]={} -ptr_subbuilding["building_doorst"].flg={off=248,rtype=WORD} --maybe flags? -ptr_subbuilding["building_doorst"].state={off=250,rtype=DWORD} diff --git a/plugins/Dfusion/luafiles/editor.lua b/plugins/Dfusion/luafiles/editor.lua deleted file mode 100644 index 06b07ce5e..000000000 --- a/plugins/Dfusion/luafiles/editor.lua +++ /dev/null @@ -1,150 +0,0 @@ -function getTypename(object) - local tbl - local ret={} - if getmetatable(object)~=nil then - local tbl=getmetatable(object) - for k,v in pairs(xtypes) do - if v==tbl then - return k - end - end - for k,v in pairs(xtypes.containers) do - if v==tbl then - return k - end - end - end - if object.name~= nil then - return object.name - end - return "?" -end -function getFields(object) - local tbl - local ret={} - if getmetatable(object)==xtypes["struct-type"].wrap then - tbl=rawget(object,"mtype") - elseif getmetatable(object)==xtypes["struct-type"] then - tbl=object - else - error("Not an class_type or a class_object") - end - for k,v in pairs(tbl.types) do - table.insert(ret,{k,v[2],getTypename(v[1])}) - --ret[v[2]]=k - --print(string.format("%s %x",k,v[2])) - end - table.sort(ret,function (a,b) return a[2]>b[2] end) - return ret -end -function editField(tbl,field,typename) - if EditType[typename] ~= nil then - EditType[typename](tbl[field]) - else - print("Cur value:"..tostring(tbl[field])) - val=getline("Enter newvalue:") - tbl[field]=val - end -end -EditType={} -EditType["df-flagarray"]=function(trg) - local fields=rawget(trg,"mtype").index.names - print("Flag count:"..trg.size) - print("Name count:"..#fields) - for i=0,#fields do - print(string.format("%3d %20s %s",i,fields[i],tostring(trg[i-1]))) - end - number=getline("enter flag id to flip:") - number=tonumber(number) - if number then - trg[fields[number]]= not trg[fields[number]] - end -end -EditType["enum-type"]=function(trg) - local fields=rawget(trg,"mtype").names - local typename=getTypename(rawget(trg,"mtype").etype) - for k,v in pairs(fields) do - print(string.format("%3d %s",k,v)) - end - local cval=trg:get() - if fields[cval]~= nil then - print(string.format("Current value:%d (%s)",cval,fields[cval])) - else - print(string.format("Current value:%d",cval)) - end - number=getline("enter new value:") - number=tonumber(number) - if number then - trg:set(number) - end -end -EditType["static-array"]=function(trg) - local item_type=rawget(trg,"mtype").item_type - local typename=getTypename(item_type) - number=getline(string.format("Select item (max %d, item-type '%s'):",trg.size,typename)) - number=tonumber(number) - if number then - EditType[typename](trg[number]) - end -end -EditType["stl-vector"]=EditType["static-array"] -EditType["df-array"]=EditType["static-array"] -EditType["struct-type"]=function(trg) - local mtype=rawget(trg,"mtype") - local fields=getFields(trg) - for k,v in pairs(fields) do - print(string.format("%4d %25s %s",k,v[1],v[3])) - end - number=getline("Choose field to edit:") - number=tonumber(number) - if number then - local v=fields[number] - editField(trg,v[1],v[3]) - end -end -EditType["pointer"]=function(trg) - local mtype=rawget(trg,"mtype").ptype - local typename=getTypename(mtype) - if(trg:tonumber()==0) then - print("pointer points to nowhere!") - return - end - print("Auto dereferencing pointer! type:"..typename) - if EditType[typename] ~= nil then - EditType[typename](trg:deref()) - else - print("Cur value:"..tostring(trg:deref())) - val=getline("Enter newvalue:") - trg:setref(val) - end -end - -function EditDF() - local i=1 - local tbl={} - for k,v in pairs(rawget(df,"types")) do - print(string.format("%4d %25s %s",i,k,getTypename(v))) - tbl[i]={k,getTypename(v)} - i=i+1 - end - number=dfhack.lineedit("select item to edit (q to quit):") - if number and tonumber(number) then - local entry=tbl[tonumber(number)] - if entry==nil then - return - end - editField(df,entry[1],entry[2]) - --[=[ - if EditType[entry[2]] ~= nil then - EditType[entry[2]](df[entry[1]]) - else - print("Cur value:"..tostring(df[entry[1]])) - val=getline("Enter newvalue:") - df[entry[1]]=val - end - --]=] - end -end -function EditObject(obj) - EditType[getTypename(obj)](obj) -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/itempatterns.lua b/plugins/Dfusion/luafiles/itempatterns.lua deleted file mode 100644 index da62c26d2..000000000 --- a/plugins/Dfusion/luafiles/itempatterns.lua +++ /dev/null @@ -1,62 +0,0 @@ ---dofile("patterns2.lua") moved to common.lua -ptr_item={} -ptr_item.RTI={off=0,rtype=DWORD} -ptr_item.x={off=4,rtype=WORD} -ptr_item.y={off=6,rtype=WORD} -ptr_item.z={off=8,rtype=WORD} -ptr_item.ref={off=0x28,rtype=ptr_vector} - -ptr_item.mat={off=0x78,rtype=WORD} -ptr_item.submat={off=0x7A,rtype=WORD} -ptr_item.submat2={off=0x7C,rtype=DWORD} -ptr_item.legendid={off=0x80,rtype=DWORD} -- i don't remember writing this... -ptr_item.decorations={off=0x90,rtype=ptr_vector} -ptr_item.flags={off=0xC,rtype=ptt_dfflag.new(8)} -ptr_item.ptr_covering={off=0x64,rtype=DWORD} -ptr_item.stack={off=0x58,rtype=WORD} -function ptr_item.getname(self,RTI) - if RTI == nil then - return string.sub(RTTI_GetName(self.RTI),5,-3) - else - return string.sub(RTTI_GetName(RTI),5,-3) - end -end -ptr_subitems={} -ptr_subitems["item_slabst"]={} -ptr_subitems["item_slabst"].msgptr={off=0xA0,rtype=ptt_dfstring} -ptr_subitems["item_slabst"].signtype={off=0xC0,rtype=DWORD} - -ptr_subitems["item_fisthst"]={} -ptr_subitems["item_fisthst"].fisthtype={off=0x78,rtype=WORD} - -ptr_subitems["item_eggst"]={} -ptr_subitems["item_eggst"].race={off=0x78,rtype=DWORD} -ptr_subitems["item_eggst"].isfertile={off=0xa0,rtype=DWORD} --0 or 1 -ptr_subitems["item_eggst"].hatchtime={off=0xa4,rtype=DWORD} - -ptr_decoration_gen={} -ptr_decoration_gen.RTI={off=0,rtype=DWORD} -ptr_decoration_gen.material={off=0x04,rtype=WORD} -- same for all? -ptr_decoration_gen.submat={off=0x08,rtype=DWORD} -function ptr_decoration_gen.getname(self,RTI) - if RTI == nil then - return string.sub(RTTI_GetName(self.RTI),21,-5) - else - return string.sub(RTTI_GetName(RTI),21,-5) - end -end -ptr_decoration={} -ptr_decoration["covered"]={} -ptr_decoration["covered"].material={off=0x04,rtype=WORD} -ptr_decoration["covered"].submat={off=0x08,rtype=DWORD} -ptr_decoration["art_image"]={} -ptr_decoration["art_image"].material={off=0x04,rtype=WORD} -ptr_decoration["art_image"].submat={off=0x08,rtype=DWORD} -ptr_decoration["art_image"].image={off=0x24,rtype=DWORD} -ptr_decoration["bands"]={} -ptr_decoration["bands"].material={off=0x04,rtype=WORD} -ptr_decoration["bands"].submat={off=0x08,rtype=DWORD} -ptr_cover={} --covering of various types (blood, water, etc) -ptr_cover.mat={off=0,rtype=WORD} -ptr_cover.submat={off=4,rtype=DWORD} -ptr_cover.state={off=8,rtype=WORD} \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/patterns.lua b/plugins/Dfusion/luafiles/patterns.lua deleted file mode 100644 index 34aa0c274..000000000 --- a/plugins/Dfusion/luafiles/patterns.lua +++ /dev/null @@ -1,248 +0,0 @@ -ptt_dfstring={} -if WINDOWS then - ptt_dfstring.ptr={off=0,rtype=DWORD} - ptt_dfstring.size={off=16,rtype=DWORD} - ptt_dfstring.alloc={off=20,rtype=DWORD} - - function ptt_dfstring:getval() - --print(string.format("GETTING FROM:%x",self.__offset)) - if self.size<16 then - --print(string.format("GETTING FROM:%x",self.__offset)) - return string.sub(engine.peekstr(self.__offset),1,self.size) - else - --print(string.format("GETTING FROM:%x",self.ptr)) - return string.sub(engine.peekstr(self.ptr),1,self.size) - end - end - function ptt_dfstring:setval(newstring) - local offset=self.__offset - local strl=string.len(newstring) - if strl<16 then - --print(string.format("GETTING FROM:%x",self.__offset)) - - engine.poked(offset+ptt_dfstring.size.off,strl) - engine.poked(offset+ptt_dfstring.alloc.off,15) - engine.pokestr(offset,newstring) - - else - local loc - if engine.peekd(offset+ptt_dfstring.alloc.off) > strl then - loc=engine.peekd(offset) - print("Will fit:"..loc.." len:"..strl) - else - loc=Allocate(strl+1) - engine.poked(offset+ptt_dfstring.alloc.off,strl) - print("Will not fit:"..loc.." len:"..strl) - end - --print(string.format("GETTING FROM:%x",self.ptr)) - engine.poked(self.__offset+ptt_dfstring.size.off,strl) - engine.pokestr(loc,newstring) - engine.poked(self.__offset,loc) - end - end -else - --ptt_dfstring.ptr={off=0,rtype=DWORD} - function ptt_dfstring:getval() - return engine.peekstr_stl(self.__offset) - end -end ---if(COMPATMODE) then ---ptr_vector={} ---ptr_vector.st={off=4,rtype=DWORD} ---ptr_vector.en={off=8,rtype=DWORD} ---else -ptr_vector={} -ptr_vector.st={off=0,rtype=DWORD} -ptr_vector.en={off=4,rtype=DWORD} -ptr_vector.alloc={off=8,rtype=DWORD} ---end -function ptr_vector:clone(settype) - local ret={} - for k,v in pairs(self) do - ret[k]=v - end - ret.type=settype - return ret -end -function ptr_vector:size() - return (self.en-self.st)/engine.sizeof(self.type) -end -ptr_vector.type=DWORD -function ptr_vector:getval(num) - if self.st==0 then return error("Vector empty.") end - --print("Wants:"..num.." size:"..self:size()) - if num>=self:size() then error("Index out of bounds in vector.") end - return engine.peek(self.st+engine.sizeof(self.type)*num,self.type) -end -function ptr_vector:setval(num,val) - return engine.poke(self.st+engine.sizeof(self.type)*num,self.type,val) -end -function ptr_vector:append(val) - if self.alloc - self.en > 0 then - local num=self:size() - self.en=self.en+engine.sizeof(self.type) - self:setval(val,num) - else - error("larger than allocated arrays not implemented yet") - local num=self:size() - local ptr=Allocate(num*2) - - end -end -ptt_dfflag={} -function ptt_dfflag.flip(self,num) --flip one bit in flags - local of=math.floor (num/8); - - self[of]=bit.bxor(self[of],bit.lshift(1,num%8)) -end - -function ptt_dfflag.get(self,num) -- get one bit in flags - local of=math.floor (num/8); - - if bit.band(self[of],bit.lshift(1,num%8))~=0 then - return true - else - return false - end -end -function ptt_dfflag.set(self,num,val) --set to on or off one bit in flags - if (self:get(num)~=val) then - self:flip(num) - end -end -function ptt_dfflag.new(size) -- create new flag pattern of size(in bytes) - local ret={} - for i=0,size-1 do - ret[i]={off=i,rtype=BYTE}; - end - ret.flip=ptt_dfflag.flip --change to metatable stuff... - ret.get=ptt_dfflag.get - ret.set=ptt_dfflag.set - return ret; -end ---[[ - Creature: - 0 name (df_string) 28 - 28 nick (df_string) 56 - 56 surname- namearray(7*dword(4)) 84 ... - 140 race (dword) 144 - 224 flags - 264 civ (dword) - 252 ID - 592 following ID - 904 bleed vector? hurt vector or sth... - 0x790 legends id? - 2128 known names? or knowledge? - flags: - 0 Can the dwarf move or are they waiting for their movement timer - 1 Dead (might also be set for incoming/leaving critters that are alive) - 2 Currently in mood - 3 Had a mood - 4 "marauder" -- wide class of invader/inside creature attackers - 5 Drowning - 6 Active merchant - 7 "forest" (used for units no longer linked to merchant/diplomacy, they just try to leave mostly) - 8 Left (left the map) - 9 Rider - 10 Incoming - 11 Diplomat - 12 Zombie - 13 Skeleton - 14 Can swap tiles during movement (prevents multiple swaps) - 15 On the ground (can be conscious) - 16 Projectile - 17 Active invader (for organized ones) - 18 Hidden in ambush - 19 Invader origin (could be inactive and fleeing) - 20 Will flee if invasion turns around - 21 Active marauder/invader moving inward - 22 Marauder resident/invader moving in all the way - 23 Check against flows next time you get a chance - 24 Ridden - 25 Caged - 26 Tame - 27 Chained - 28 Royal guard - 29 Fortress guard - 30 Suppress wield for beatings/etc - 31 Is an important historical figure - 32 swiming - -]]-- -ptr_Creature={} -local posoff=0 --VersionInfo.getGroup("Creatures"):getGroup("creature"):getOffset("position") -ptr_Creature.x={off=posoff,rtype=WORD} --ok -ptr_Creature.y={off=posoff+2,rtype=WORD} --ok -ptr_Creature.z={off=posoff+4,rtype=WORD} --ok -ptr_Creature.flags={off=0,rtype=ptt_dfflag.new(10)} -ptr_Creature.name={off=0,rtype=ptt_dfstring} -ptr_Creature.ID={off=252,rtype=DWORD} --ok i guess -ptr_Creature.followID={off=592,rtype=DWORD} --ok -ptr_Creature.race={off=140,rtype=DWORD} --ok -ptr_Creature.civ={off=264,rtype=DWORD} -ptr_Creature.legends={off=344,rtype=ptr_vector} --ok -ptr_Creature.hurt1={off=0x308,rtype=ptr_vector:clone(BYTE)} --byte vector... -ptr_Creature.hurt2={off=0x338,rtype=ptr_vector} -ptr_Creature.wounds={off=0x388,rtype=ptr_vector} -ptr_Creature.itemlist1={off=0x1D0,rtype=ptr_vector} -ptr_Creature.itemlist2={off=0x288,rtype=ptr_vector} -ptr_Creature.bloodlvl={off=0x490,rtype=DWORD} -ptr_Creature.bleedlvl={off=0x494,rtype=DWORD} - -ptr_CrGloss={} -ptr_CrGloss.token={off=0,rtype=ptt_dfstring} -ptr_CrGloss.castes={off=296,rtype=ptr_vector} - -ptr_CrCaste={} -ptr_CrCaste.name={off=0,rtype=ptt_dfstring} -ptr_CrCaste.flags_ptr={off=0x5A0,rtype=DWORD} --size 17? ---[=[ - Flags: - 57 - is sentient (allows setting labours) ---]=] -ptr_LEntry={} -- all size 256 -ptr_LEntry.name={off=36,rtype=ptt_dfstring} -ptr_LEntry.id1={off=160,rtype=DWORD} -ptr_LEntry.id2={off=164,rtype=DWORD} -ptr_LEntry.somlist={off=220,rtype=DWORD} - -ptr_dfname={} -for i=0,6 do -ptr_dfname[i]={off=i*4,rtype=DWORD} -end ---[[ - Site docs: - 0x38 name struct todo... - 0x78 type: - 0 - mountain halls (yours) - 1 - dark fort - 2 - cave - 3 - mountain hall (other) - 4 - forest - 5 - hamlet - 6 - imp location - 7 - lair - 8 - fort - 9 - camp - 0x7a some sort of id? - 0x84 some vec (ids) - 0x94 some other vec (ids) - 0xa4 some vec (prts) - 0x118 ptr to sth - - 0x14c ptr to mapdata -]]-- - - -ptr_site={} -ptr_site.type={off=0x78,rtype=WORD} -ptr_site.id={off=0x7a,rtype=DWORD} -ptr_site.name={off=0x38,rtype=ptr_dfname} -ptr_site.flagptr={off=0x118,rtype=DWORD} - -ptr_legends2={} -ptr_legends2.id={off=0,rtype=DWORD} -ptr_legends2.follow={off=0x18,rtype=DWORD} - -ptr_material={} -ptr_material.token={off=0,rtype=ptt_dfstring} diff --git a/plugins/Dfusion/luafiles/patterns2.lua b/plugins/Dfusion/luafiles/patterns2.lua deleted file mode 100644 index 09a3b0db8..000000000 --- a/plugins/Dfusion/luafiles/patterns2.lua +++ /dev/null @@ -1,29 +0,0 @@ -ptr_COL={} -- complete object locator... -ptr_COL.sig={off=0,rtype=DWORD} -ptr_COL.offset={off=4,rtype=DWORD} --offset of this vtable in the complete class -ptr_COL.cdoffset={off=8,rtype=DWORD} -- constructor displacement -ptr_COL.typePointer={off=12,rtype=DWORD} -ptr_COL.hierarchyPointer={off=16,rtype=DWORD} - -ptr_RTTI_Type={} -ptr_RTTI_Type.vftPointer={off=0,rtype=DWORD} -ptr_RTTI_Type.name={off=8,rtype=STD_STRING} - -function RTTI_GetName(vtable) - local COLoff=engine.peek(vtable-4,DWORD) - --print(string.format("Look:%x vtable:%x",vtable,engine.peek(vtable-4,DWORD))) - COL=engine.peek(COLoff,ptr_COL) - --print(string.format("COL:%x Typeptr:%x Type:%s",COLoff,COL.typePointer,engine.peek(COL.typePointer,ptr_RTTI_Type.name))) - return engine.peek(COL.typePointer,ptr_RTTI_Type.name) -end -ptr_RTTI_Hierarchy={} -ptr_RTTI_Hierarchy.sig={off=0,rtype=DWORD} -ptr_RTTI_Hierarchy.attributes={off=4,rtype=DWORD} -ptr_RTTI_Hierarchy.numBaseClasses={off=8,rtype=DWORD} -ptr_RTTI_Hierarchy.ptrBases={off=12,rtype=DWORD} - -ptr_RTTI_BaseClass={} -ptr_RTTI_BaseClass.typePointer={off=0,rtype=DWORD} -ptr_RTTI_BaseClass.numContained={off=4,rtype=DWORD} ---todo PMD ---todo flags \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/xml_struct.lua b/plugins/Dfusion/luafiles/xml_struct.lua deleted file mode 100644 index 8d0801be3..000000000 --- a/plugins/Dfusion/luafiles/xml_struct.lua +++ /dev/null @@ -1,151 +0,0 @@ -if types ~= nil then - return -end -dofile("dfusion/xml_types.lua") - -function parseTree(t) - for k,v in ipairs(t) do - if v.xarg~=nil and v.xarg["type-name"]~=nil and v.label=="ld:global-type" then - local name=v.xarg["type-name"]; - if(types[name]==nil) then - - --for kk,vv in pairs(v.xarg) do - -- print("\t"..kk.." "..tostring(vv)) - --end - types[name]=makeType(v) - --print("found "..name.." or type:"..v.xarg.meta or v.xarg.base) - - else - types[name]=makeType(v,types[name]) - end - end - end -end -function parseTreeGlobals(t) - local glob={} - --print("Parsing global-objects") - for k,v in ipairs(t) do - if v.xarg~=nil and v.label=="ld:global-object" then - local name=v.xarg["name"]; - --print("Parsing:"..name) - local subitem=v[1] - if subitem==nil then - error("Global-object subitem is nil:"..name) - end - local ret=makeType(subitem) - if ret==nil then - error("Make global returned nil!") - end - glob[name]=ret - end - end - --print("Printing globals:") - --for k,v in pairs(glob) do - -- print(k) - --end - return glob -end -function findAndParse(tname) - --if types[tname]==nil then types[tname]={} end - -- [=[ - for k,v in ipairs(main_tree) do - local name=v.xarg["type-name"]; - if v.xarg~=nil and v.xarg["type-name"]~=nil and v.label=="ld:global-type" then - - if(name ==tname) then - --print("Parsing:"..name) - --for kk,vv in pairs(v.xarg) do - -- print("\t"..kk.." "..tostring(vv)) - --end - types[name]=makeType(v,types[name]) - end - --print("found "..name.." or type:"..v.xarg.meta or v.xarg.base) - end - end - --]=] -end -df={} -df.types=rawget(df,"types") or {} --temporary measure for debug -local df_meta={} -function df_meta:__index(key) - local addr=VersionInfo.getAddress(key) - local vartype=rawget(df,"types")[key]; - if addr==0 then - error("No such global address exist") - elseif vartype==nil then - error("No such global type exist") - else - return type_read(vartype,addr) - end -end -function df_meta:__newindex(key,val) - local addr=VersionInfo.getAddress(key) - local vartype=rawget(df,"types")[key]; - if addr==0 then - error("No such global address exist") - elseif vartype==nil then - error("No such global type exist") - else - return type_write(vartype,addr,val) - end -end -setmetatable(df,df_meta) --------------------------------- -types=types or {} -dofile("dfusion/patterns/xml_angavrilov.lua") --- [=[ -main_tree=parseXmlFile("dfusion/patterns/supplementary.xml")[1] -parseTree(main_tree) -main_tree=parseXmlFile("dfusion/patterns/codegen.out.xml")[1] -parseTree(main_tree) -rawset(df,"types",parseTreeGlobals(main_tree)) ---]=] ---[=[labels={} -for k,v in ipairs(t) do - labels[v.label]=labels[v.label] or {meta={}} - if v.label=="ld:global-type" and v.xarg~=nil and v.xarg.meta ~=nil then - labels[v.label].meta[v.xarg.meta]=1 - end -end -for k,v in pairs(labels) do - print(k) - if v.meta~=nil then - for kk,vv in pairs(v.meta) do - print("=="..kk) - end - end -end--]=] -function addressOf(var,key) - if key== nil then - local addr=rawget(var,"ptr") - return addr - else - local meta=getmetatable(var) - if meta== nil then - error("Failed to get address, no metatable") - end - if meta.__address == nil then - error("Failed to get address, no __address function") - end - return meta.__address(var,key) - end -end -function printGlobals() - print("Globals:") - for k,v in pairs(rawget(df,"types")) do - print(k) - end -end -function printFields(object) - local tbl - if getmetatable(object)==xtypes["struct-type"].wrap then - tbl=rawget(object,"mtype") - elseif getmetatable(object)==xtypes["struct-type"] then - tbl=object - else - error("Not an class_type or a class_object") - end - for k,v in pairs(tbl.types) do - print(string.format("%s %x",k,v[2])) - end -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/xml_types.lua b/plugins/Dfusion/luafiles/xml_types.lua deleted file mode 100644 index cced4be2c..000000000 --- a/plugins/Dfusion/luafiles/xml_types.lua +++ /dev/null @@ -1,734 +0,0 @@ --- otherwise you just maintain alignment granularity in addition to size for all fields, --- round up current offset to field alignment, --- assign structs the max alignment of any field, and round up struct size to its alignment -function type_read(valtype,address) - if valtype.issimple then - --print("Simple read:"..tostring(valtype.ctype)) - return engine.peek(address,valtype.ctype) - else - return valtype:makewrap(address) - end -end -function type_write(valtype,address,val) - if valtype.issimple then - engine.poke(address,valtype.ctype,val) - else - engine.poke(address,DWORD,rawget(val,"ptr")) - end -end -function first_of_type(node,labelname) - for k,v in ipairs(node) do - if type(v)=="table" and v.label==labelname then - return v - end - end -end -xtypes={} -- list of all types prototypes (e.g. enum-type -> announcement_type) --- type must have size, new and makewrap (that makes a wrapper around ptr) -if WINDOWS then - dofile("dfusion/xml_types_windows.lua") -elseif LINUX then - dofile("dfusion/xml_types_linux.lua") -end - -function padAddress(curoff,typetoadd) --return new offset to place things... Maybe linux is different? - --windows -> sizeof(x)==alignof(x) - --[=[ - - if sizetoadd>8 then sizetoadd=8 end - if(math.mod(curoff,sizetoadd)==0) then - return curoff - else - return curoff+(sizetoadd-math.mod(curoff,sizetoadd)) - end - --]=] - if typetoadd==nil or typetoadd.__align==nil then - return curoff - else - - if(math.mod(curoff,typetoadd.__align)==0) then - return curoff - else - if PRINT_PADS then - print("padding off:"..curoff.." with align:"..typetoadd.__align.." pad="..(typetoadd.__align-math.mod(curoff,typetoadd.__align))) - end - return curoff+(typetoadd.__align-math.mod(curoff,typetoadd.__align)) - end - end -end - - -local sarr={} -sarr.__index=sarr -function sarr.new(node,obj) - local o=obj or {} - setmetatable(o,sarr) - --print("Making array.") - o.count=tonumber(node.xarg.count) - --print("got count:"..o.count) - o.item_type=makeType(first_of_type(node,"ld:item")) - o.__align=o.item_type.__align or 4 - o.size=o.count*o.item_type.size - --print("got subtypesize:"..o.item_type.size) - return o -end -function sarr:makewrap(address) - local o={} - o.mtype=self - o.ptr=address - setmetatable(o,self.wrap) - return o -end -sarr.wrap={} -function sarr.wrap:__index(key) - if key=="size" then - return rawget(self,"mtype").count - end - local num=tonumber(key) - local mtype=rawget(self,"mtype") - if num~=nil and num"..v.xarg.meta.." ttype:"..v.label) - local ttype=makeType(v) - if ttype.size==0 then error("Field with 0 size! type="..node.xarg["type-name"].."."..name) end - if ttype.size-math.ceil(ttype.size) ~= 0 then error("Field with real offset! type="..node.xarg["type-name"].."."..name) end - --for k,v in pairs(ttype) do - -- print(k..tostring(v)) - --end - - local off=padAddress(o.size,ttype) - --[=[if PRINT_PADS then - - if ttype.__align then - print(name.." "..ttype.__align .. " off:"..off.." "..math.mod(off,ttype.__align)) - - end - if off~=o.size then - print("var:"..name.." size:"..ttype.size) - end - end]=] - --print("var:"..name.." ->"..tostring(off).. " :"..ttype.size) - if isunion then - if ttype.size > o.size then - o.size=ttype.size - end - o.types[name]={ttype,0} - else - o.size=off - o.types[name]={ttype,o.size}--+o.baseoffset - o.size=o.size+ttype.size - end - if firsttype== nil then - firsttype=o.types[name][1] - end - - end - end - if isunion then - o.__align=0 - for k,v in pairs(o.types) do - if v[1].__align~= nil and v[1].__align>o.__align then - o.__align=v[1].__align - end - end - else - if o.base[1]~= nil then - o.__align=o.base[1].__align - elseif firsttype~= nil then - o.__align=firsttype.__align - --if o.__align~=nil then - --print("\t\t setting align to:"..(o.__align or "")) - --else - --o.__align=4 - --print("\t\t NIL ALIGNMENT!") - --end - end - end - - return o -end - -type_class.wrap={} -function type_class.wrap:__address(key) - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - if mytype.types[key] ~= nil then - return myptr+mytype.types[key][2] - else - error("No such field exists") - end -end -function type_class.wrap:__index(key) - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - if mytype.types[key] ~= nil then - return type_read(mytype.types[key][1],myptr+mytype.types[key][2]) - else - error("No such field exists") - end -end -function type_class.wrap:__newindex(key,value) - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - if mytype.types[key] ~= nil then - return type_write(mytype.types[key][1],myptr+mytype.types[key][2],value) - else - error("No such field exists") - end -end -function type_class:makewrap(ptr) - local o={} - o.ptr=ptr - o.mtype=self - setmetatable(o,self.wrap) - return o -end -xtypes["struct-type"]=type_class -xtypes["class-type"]=type_class -local type_pointer={} -type_pointer.__index=type_pointer -function type_pointer.new(node,obj) - local o=obj or {} - setmetatable(o,type_pointer) - local subnode=first_of_type(node,"ld:item") - if subnode~=nil then - o.ptype=makeType(subnode,nil,true) - end - - o.size=4 - o.__align=4 - return o -end -type_pointer.wrap={} -type_pointer.wrap.__index=type_pointer.wrap -function type_pointer.wrap:tonumber() - local myptr=rawget(self,"ptr") - return engine.peekd(myptr)--type_read(DWORD,myptr) -end -function type_pointer.wrap:__setup(trg) - if trg~= nil then - self:fromnumber(trg) - else - self:fromnumber(0) - end -end -function type_pointer.wrap:fromnumber(num) - local myptr=rawget(self,"ptr") - return engine.poked(myptr,num)--type_write(DWORD,myptr,num) -end -function type_pointer.wrap:deref() - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - return type_read(mytype.ptype,engine.peekd(myptr)) -end -function type_pointer.wrap:setref(val) - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - return type_write(mytype.ptype,engine.peekd(myptr),val) -end -function type_pointer.wrap:newref(val) - local myptr=rawget(self,"ptr") - local mytype=rawget(self,"mtype") - local ptr=engine.alloc(mytype.ptype.size) - self:fromnumber(ptr) - return ptr -end -function type_pointer:makewrap(ptr) - local o={} - o.ptr=ptr - o.mtype=self - setmetatable(o,self.wrap) - return o -end -xtypes["pointer"]=type_pointer --------------------------------------------- ---stl-vector (beginptr,endptr,allocptr) ---df-flagarray (ptr,size) -xtypes.containers=xtypes.containers or {} -local dfarr={} -dfarr.__index=dfarr -function dfarr.new(node,obj) - local o=obj or {} - setmetatable(o,dfarr) - o.size=8 - o.__align=4 - o.item_type=makeType(first_of_type(node,"ld:item")) - return o -end -function dfarr:makewrap(address) - local o={} - o.mtype=self - o.ptr=address - setmetatable(o,self.wrap) - return o -end -dfarr.wrap={} -function dfarr.wrap:__setup(size) - local mtype=rawget(self,"mtype") - engine.pokew(rawget(self,"ptr")+4,size) - local newptr=engine.alloc(size*mtype.item_type.size) - engine.poked(rawget(self,"ptr"),newptr) -end -function dfarr.wrap:__index(key) - local num=tonumber(key) - local mtype=rawget(self,"mtype") - local size=engine.peekw(rawget(self,"ptr")+4) - if key=="size" then - return size - end - local item_start=engine.peekd(rawget(self,"ptr")) - if num~=nil and num"..tostring(v)) - end - error("Node parser not found: "..label) - end - --[=[ - if getSimpleType(node)~=nil then - return getSimpleType(node) - end - print("Trying to make:"..node.xarg.meta) - if xtypes[node.xarg.meta]~=nil then - return xtypes[node.xarg.meta].new(node,obj) - end - - if node.xarg.meta=="global" then - --print(node.xarg["type-name"]) - - if types[node.xarg["type-name"]]== nil then - error("type:"..node.xarg["type-name"].." should already be ready") - end - return types[node.xarg["type-name"]] - end - ]=] - --[=[for k,v in pairs(node) do - print(k.."=>"..tostring(v)) - if type(v)=="table" then - for kk,vv in pairs(v) do - print("\t"..kk.."=>"..tostring(vv)) - end - end - end]=] - -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/xml_types_windows.lua b/plugins/Dfusion/luafiles/xml_types_windows.lua deleted file mode 100644 index 90bc80312..000000000 --- a/plugins/Dfusion/luafiles/xml_types_windows.lua +++ /dev/null @@ -1,159 +0,0 @@ -xtypes.containers=xtypes.containers or {} -local stl_vec={} ---[=[ - (make-instance 'pointer :name $start) - (make-instance 'pointer :name $end) - (make-instance 'pointer :name $block-end) - (make-instance 'padding :name $pad :size 4 :alignment 4) ---]=] -stl_vec.__index=stl_vec - -function stl_vec.new(node,obj) - local o=obj or {} - - o.size=16 - o.__align=4 - local titem=first_of_type(node,"ld:item") - if titem~=nil then - o.item_type=makeType(titem) - else - o.item_type=getSimpleType("uint32_t") - end - setmetatable(o,stl_vec) - return o -end -function stl_vec:makewrap(address) - local o=obj or {} - o.mtype=self - o.ptr=address - setmetatable(o,self.wrap) - return o -end -stl_vec.wrap={} -function stl_vec.wrap:__index(key) - local num=tonumber(key) - local mtype=rawget(self,"mtype") - local ptr=rawget(self,"ptr") - local p_begin=engine.peek(ptr,DWORD) - local p_end=engine.peek(ptr+4,DWORD) - local size=(p_end-p_begin)/mtype.item_type.size - if key=="size" then - return size - end - - --allocend=type_read(ptr+8,DWORD) - if num~=nil and num Date: Sat, 15 Sep 2012 18:05:53 +0300 Subject: [PATCH 14/79] Removed unused triggers folder --- plugins/Dfusion/luafiles/triggers/compile.bat | 1 - .../Dfusion/luafiles/triggers/functions.lua | 20 ---- .../luafiles/triggers/functions_menu.lua | 12 -- plugins/Dfusion/luafiles/triggers/plugin.lua | 107 ------------------ .../Dfusion/luafiles/triggers/triggers.asm | 68 ----------- plugins/Dfusion/luafiles/triggers/triggers.o | Bin 720 -> 0 bytes .../luafiles/triggers/universalfunc.asm | 14 --- 7 files changed, 222 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/triggers/compile.bat delete mode 100644 plugins/Dfusion/luafiles/triggers/functions.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/functions_menu.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/triggers.asm delete mode 100644 plugins/Dfusion/luafiles/triggers/triggers.o delete mode 100644 plugins/Dfusion/luafiles/triggers/universalfunc.asm diff --git a/plugins/Dfusion/luafiles/triggers/compile.bat b/plugins/Dfusion/luafiles/triggers/compile.bat deleted file mode 100644 index 3b0fec1a2..000000000 --- a/plugins/Dfusion/luafiles/triggers/compile.bat +++ /dev/null @@ -1 +0,0 @@ -as -anl --32 -o triggers.o triggers.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/triggers/functions.lua b/plugins/Dfusion/luafiles/triggers/functions.lua deleted file mode 100644 index e6c3a62c9..000000000 --- a/plugins/Dfusion/luafiles/triggers/functions.lua +++ /dev/null @@ -1,20 +0,0 @@ -function func.Find_Print() - pos=offsets.find(offsets.base(),0x73,0x02,0x8b,0xce,0x53,0x6a,0x01,0x6a,0x06,CALL) -- a hack for now... - return engine.peekd(pos+10)+pos+14-offsets.base() -end -function func.PrintMessage(msg,color1,color2) - func.f_print_pos= func.f_print_pos or func.Find_Print() - print(string.format("Print @:%x",func.f_print_pos)) - debuger.suspend() - d=NewCallTable() -- make a call table - t=Allocate(string.len(msg)) - engine.pokestr(t,msg) - --print(string.format("Message location:%x",t)) - d["ECX"]=t --set ecx to message location - d["STACK5"]=color1 -- push to stack color1 - d["STACK4"]=color2 -- push to stack color2 - d["STACK3"]=0 -- this is usually 0 maybe a struct pointing to location of this message? - PushFunction(func.f_print_pos+offsets.base(),d) -- prep to call function - -- was 0x27F030 - debuger.resume() -end diff --git a/plugins/Dfusion/luafiles/triggers/functions_menu.lua b/plugins/Dfusion/luafiles/triggers/functions_menu.lua deleted file mode 100644 index 3256c6117..000000000 --- a/plugins/Dfusion/luafiles/triggers/functions_menu.lua +++ /dev/null @@ -1,12 +0,0 @@ -func={} -dofile("dfusion/triggers/functions.lua") -func.menu=MakeMenu() -function func.PrintMessage_() - print("Type a message:") - msg=io.stdin:read() - func.PrintMessage(msg,6,1) -end -if not(FILE) then -- if not in script mode - func.menu:add("Print message",func.PrintMessage_) - func.menu:display() -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/triggers/plugin.lua b/plugins/Dfusion/luafiles/triggers/plugin.lua deleted file mode 100644 index e8f31eac1..000000000 --- a/plugins/Dfusion/luafiles/triggers/plugin.lua +++ /dev/null @@ -1,107 +0,0 @@ -if FILE then - return -end -callinfo={} -callinfo.regs={} -callinfo.regs["EAX"]=0 -callinfo.regs["EBX"]=1 -callinfo.regs["ECX"]=2 -callinfo.regs["EDX"]=3 -callinfo.regs["ESI"]=4 -callinfo.regs["EDI"]=5 -callinfo.regs["STACK1"]=6 -callinfo.regs["STACK2"]=7 -callinfo.regs["STACK3"]=8 -callinfo.regs["STACK4"]=9 -callinfo.regs["STACK5"]=10 - -mypos=engine.getmod("triggers_main") - -function GetCount() - return engine.peek(0,triggers.count) -end -function SetCount(val) - engine.poke(0,triggers.count,val) -end -function NewCallTable(tbl) - ret=tbl or {} - for k,v in pairs(callinfo.regs) do - ret[k]=0 - end - return ret -end -function PushFunction(off,data) - local i=GetCount() - engine.poked(triggers.table.off+i*44,off) -- add function to table - engine.poked(triggers.data.off+0,data["EAX"]) --set register data... - engine.poked(triggers.data.off+4,data["EBX"]) - engine.poked(triggers.data.off+8,data["ECX"]) - engine.poked(triggers.data.off+12,data["EDX"]) - engine.poked(triggers.data.off+16,data["ESI"]) - engine.poked(triggers.data.off+20,data["EDI"]) - engine.poked(triggers.data.off+24,data["STACK1"]) - engine.poked(triggers.data.off+28,data["STACK2"]) - engine.poked(triggers.data.off+32,data["STACK3"]) - engine.poked(triggers.data.off+36,data["STACK4"]) - engine.poked(triggers.data.off+40,data["STACK5"]) - SetCount(i+1) -end -function loadTriggers() - if triggers then return end - triggers={} - p=engine.getmod("triggerdata") - triggers.count={off=engine.peekd(p),rtype=DWORD} - triggers.table={off=engine.peekd(p+4),rtype=DWORD} - triggers.ret={off=engine.peekd(p+8),rtype=DWORD} - triggers.data={off=engine.peekd(p+12),rtype=DWORD} -end -if mypos then - loadTriggers() - dofile("dfusion/triggers/functions_menu.lua") - --return -else - triggers={} - - off=0x56D345+offsets.base() - print(string.format("Function start %x",off)) - ModData=engine.installMod("dfusion/triggers/triggers.o","triggers_main") - print("installed") - modpos=ModData.pos - modsize=ModData.size - fdata=engine.newmod("function_body",256) - - - engine.poked(modpos+engine.FindMarker(ModData,"trigercount"),modpos+modsize) -- count of functions - engine.poked(modpos+engine.FindMarker(ModData,"f_loc"),modpos+modsize+4) -- function table start - engine.poked(modpos+engine.FindMarker(ModData,"f_data"),fdata) -- function data start - - engine.poked(modpos+engine.FindMarker(ModData,"saveplace31"),modpos+modsize+260) -- save function loc - engine.poked(modpos+engine.FindMarker(ModData,"saveplace32"),modpos+modsize+260) -- save function loc - engine.poked(modpos+engine.FindMarker(ModData,"saveplace33"),modpos+modsize+260) -- save function loc - engine.poked(modpos+engine.FindMarker(ModData,"saveplace"),modpos+modsize+256) -- save function loc - engine.poked(modpos+engine.FindMarker(ModData,"trigcount2"),modpos+modsize) -- count of functions (for zeroing) - engine.poked(modpos+engine.FindMarker(ModData,"saveplace2"),modpos+modsize+256) -- save function loc - engine.poked(modpos+engine.FindMarker(ModData,"results"),modpos+modsize+256) --overwrite function call with results - - triggers.count={off=modpos+modsize,rtype=DWORD} - triggers.table={off=modpos+modsize+4,rtype=DWORD} - triggers.ret={off=modpos+modsize+256,rtype=DWORD} - triggers.data={off=fdata,rtype=DWORD} - pp=Allocate(4*4) - engine.poked(pp,triggers.count.off) - engine.poked(pp+4,triggers.table.off) - engine.poked(pp+8,triggers.ret.off) - engine.poked(pp+12,triggers.data.off) - engine.newmod("triggerdata",0,pp) - function pokeCall(off) - engine.pokeb(off,0xe8) - --b=engine.peekb(off+1) - engine.poked(off+1,modpos-off-5) - --engine.pokeb(off+5,b) - end - print(string.format("Mod @:%x",modpos)) - dat=engine.peekarb(off,5) - engine.pokearb(modpos,dat,5) - pokeCall(off) -end - diff --git a/plugins/Dfusion/luafiles/triggers/triggers.asm b/plugins/Dfusion/luafiles/triggers/triggers.asm deleted file mode 100644 index 58b9e5f61..000000000 --- a/plugins/Dfusion/luafiles/triggers/triggers.asm +++ /dev/null @@ -1,68 +0,0 @@ -.intel_syntax -nop #5 nops for instruction thats replaced by call -nop -nop -nop -nop -pushad -pushfd -saveplace31: -mov [0xDEADBEEF], esp -trigercount: -mov eax, [0xDEADBEEF] #mov count of triggers. -f_loc: -mov esi, 0xdeadbeef #mov location of functions. -f_data: -mov ebx, 0xDEADBEEF #mov a start of function data -test eax,eax -jz lend -lstart: -dec eax -push ebx -push eax - -mov eax,[esi+eax*4] -saveplace: -mov [0xDEADBEEF],eax #save function for later -pop eax -push eax -mov edx,44 -mul edx -add eax,ebx -#stack preparation -mov ebx,[eax+24] -push ebx -mov ebx,[eax+28] -push ebx -mov ebx,[eax+32] -push ebx -mov ebx,[eax+36] -push ebx -mov ebx,[eax+40] -push ebx -mov ebx,[eax+4] -mov ecx,[eax+8] -mov edx,[eax+12] -mov esi,[eax+16] -mov edi,[eax+20] -mov eax,[eax] -saveplace2: -call [0xdeadbeef] #same save loc -results: -mov [0xDEADBEEF],eax #get result -saveplace33: -mov esp, [0xDEADBEEF] -add esp, -8 -pop eax -pop ebx -cmp eax,0 -jnz lstart -lend: -xor eax,eax -trigcount2: -mov dword ptr [0xDEADBEEF], eax # zero triggers -saveplace32: -mov esp, [0xDEADBEEF] -popfd -popad -ret diff --git a/plugins/Dfusion/luafiles/triggers/triggers.o b/plugins/Dfusion/luafiles/triggers/triggers.o deleted file mode 100644 index 5a47daa69bb4ac4f86773a23f66d5a73c8b6d7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 720 zcmeZaWM%*X5k?>evsfARN>VFIz-$Q70i=46_#mbNgF%8`N@7VOnm9Vwfx+N_UQ%%} zx;Q$&0jO>Q2qetuRDHj1?Y)H{Vjqau4I)|(l!SQ%2XwQvEe3HT0(R*DjrjhE@kV!q zL~wV63=k^-u?i4t05MCq2S;}RPj`VpcZEnd!+%kbF0j^au<^}Dendn!|6nLxWq1Hd zcy8k12@~|vGILU)4*d6@fgznSEioG=faGu%Ae$YCfkDT>1{N0plDu${wD_F-WFSun zNdyvP;&73i)VvfRhoKB1Qe2W)1Pp!!xCoHJAOpl|C?X(UG+d-8wYW5=q?iE|9|-pt z0(ogDBDO$Y1&W9VkXH*AL5_QnIn0c0KoS)o$2${HiW#I34IsxkD1~8D69N=3PAp3; n$Vp62H8x}@DauSwElSQW%_~8OBUnZVrm- Date: Sun, 16 Sep 2012 13:20:55 +0300 Subject: [PATCH 15/79] More useless stuff removed --- plugins/Dfusion/luafiles/items/plugin.lua | 212 ---------------------- plugins/Dfusion/luafiles/tools/init.lua | 2 + 2 files changed, 2 insertions(+), 212 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/items/plugin.lua diff --git a/plugins/Dfusion/luafiles/items/plugin.lua b/plugins/Dfusion/luafiles/items/plugin.lua deleted file mode 100644 index 8b92785bd..000000000 --- a/plugins/Dfusion/luafiles/items/plugin.lua +++ /dev/null @@ -1,212 +0,0 @@ -items={} --> first lets make a menu table -items.menu=MakeMenu() - - -function items.dest() - myoff=offsets.getEx("Items") -- first find out where "item vector" is - vector=engine.peek(myoff,ptr_vector) -- get list of items - for i=0,vector:size()-1 do --look at each item - flg=engine.peek(vector:getval(i),ptr_item.flags) - flg:set(17,1) - engine.poke(vector:getval(i),ptr_item.flags,flg) - end -end -function items.eggs() - myoff=offsets.getEx("Items") -- first find out where "item vector" is - vector=engine.peek(myoff,ptr_vector) -- get list of items - for i=0,vector:size()-1 do --look at each item - rti=engine.peek(vector:getval(i),ptr_item.RTI) - if ptr_item.getname(nil,rti)=="item_eggst" then - egg=engine.peek(vector:getval(i),ptr_subitems["item_eggst"]) - egg.isfertile=1 - egg.hatchtime=0xffffff - --egg.race=123 -- change race for fun times - engine.poke(vector:getval(i),ptr_subitems["item_eggst"],egg) - end - end -end -function editFlags(offset) - while true do - flags=engine.peek(offset,ptr_item.flags) - for i=0,8*8-1 do - if flags:get(i) then - print(i.." is true") - else - print(i.." is false") - end - end - print(" enter number to switch flag or not a number to quit:") - q=tonumber(io.stdin:read()) - if q==nil then return end - flags:flip(q) - engine.poke(offset,ptr_item.flags,flags) - end -end -function editCovering(offset) - off=engine.peek(offset,ptr_item.ptr_covering) - if off == 0 then - print("No coverings found.") - end - vec=engine.peek(off,ptr_vector) - print("Covering list:") - for i=0,vec:size()-1 do - cov=engine.peek(vec:getval(i),ptr_cover) - print(string.format("%d. mat=%d submat=%d state=%d",i,cov.mat,cov.submat,cov.state)) - end - print("To edit type number:") - q=tonumber(io.stdin:read()) - if q==nil then return end - if q>=vec:size() or q<0 then return end - off=vec:getval(q) - cov=engine.peek(off,ptr_cover) - print("Enter mat:") - q=tonumber(io.stdin:read()) - if q==nil then q=0xffff end - print("Enter submat:") - v=tonumber(io.stdin:read()) - if v==nil then v=0xffff end - print("Enter state:") - y=tonumber(io.stdin:read()) - if y==nil then y=0 end - cov.mat=q - cov.submat=v - cov.state=y - engine.poke(off,ptr_cover,cov) -end -function editMaterial(offset) - print("Mat id 0 to 18 is in normal materials (inorganic, amber etc...) after that creature mat (with submat2 being race)") - print("from 219 with submat2=0xffffffff (type not a number) it reads legends id, from 419 submat2 means plant id") - print("Probably submat is not used :? ") - mat=engine.peek(offset,ptr_item.mat) - submat=engine.peek(offset,ptr_item.submat) - submat2=engine.peek(offset,ptr_item.submat2) - lid=engine.peek(offset,ptr_item.legendid) - print(string.format("Now is mat=%d, submat=%d submat2=%d legend id=%d",mat,submat,submat2,lid)) - print("Enter mat:") - q=tonumber(io.stdin:read()) - if q==nil then return end - print("Enter submat:") - v=tonumber(io.stdin:read()) - if v==nil then v=0xffff end - print("Enter submat2:") - z=tonumber(io.stdin:read()) - if z==nil then z=0xffffffff end - print("Enter legendid:") - y=tonumber(io.stdin:read()) - if y==nil then y=0xffffffff end - engine.poke(offset,ptr_item.mat,q) - engine.poke(offset,ptr_item.submat,v) - engine.poke(offset,ptr_item.legendid,y) - engine.poke(offset,ptr_item.submat2,z) - print("Done") -end -function items.select() - myoff=offsets.getEx("Items") - vector=engine.peek(myoff,ptr_vector) - tx,ty,tz=getxyz() - T={} - for i=0,vector:size()-1 do --this finds all item offsets that are on pointer - itoff=vector:getval(i) - x=engine.peek(itoff,ptr_item.x) - y=engine.peek(itoff,ptr_item.y) - z=engine.peek(itoff,ptr_item.z) - if x==tx and y==ty and z==tz then - table.insert(T,itoff) - end - end - print("Items under cursor:") - i=1 - for _,v in pairs(T) do - RTI=engine.peek(v,ptr_item.RTI) - print(i..". "..ptr_item.getname(nil,RTI)) - i=i+1 - end - print("Type number to edit or 'q' to exit") - while true do - q=io.stdin:read() - if q=='q' then return end - if tonumber(q) ~=nil and tonumber(q) Date: Sun, 23 Sep 2012 23:22:14 +0300 Subject: [PATCH 16/79] gm-editor fixes and improvements --- scripts/gui/gm-editor.lua | 341 +++++++++++++++++++------------------- 1 file changed, 172 insertions(+), 169 deletions(-) diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index d95cb652b..44345b3e8 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -1,5 +1,4 @@ -- Interface powered item editor. --- TODO use this: MechanismList = defclass(MechanismList, guidm.MenuOverlay) local gui = require 'gui' local dialog = require 'gui.dialogs' @@ -40,177 +39,181 @@ end local MODE_BROWSE=0 local MODE_EDIT=1 -local item_screen={ +GmEditorUi = defclass(GmEditorUi, gui.FramedScreen) +GmEditorUi.ATTRS={ frame_style = gui.GREY_LINE_FRAME, frame_title = "GameMaster's editor", - stack={}, - item_count=0, - mode=MODE_BROWSE, - - keys={}, - - insertNew=function(self,typename) - local tp=typename - if typename== nil then - dialog.showInputPrompt("Class type","Input class type\n:",COLOR_WHITE,"",dfhack.curry(self.insertNew,self)) - return - end - local ntype=df[tp] - if ntype== nil then - dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED) - return - end - - local trg=self:currentTarget() - if trg.target and trg.target._kind and trg.target._kind=="container" then - local thing=ntype:new() - dfhack.call_with_finalizer(1,false,df.delete,thing,trg.target.insert,trg.target,'#',thing) - - end - end, - deleteSelected=function(self) - local trg=self:currentTarget() - if trg.target and trg.target._kind and trg.target._kind=="container" then - trg.target:erase(trg.keys[trg.selected]) - end - end, - currentTarget=function(self) - return self.stack[#self.stack] - end, - changeSelected = function (self,delta) - local trg=self:currentTarget() - if trg.item_count <= 1 then return end - trg.selected = 1 + (trg.selected + delta - 1) % trg.item_count - end, - editSelected = function(self) - local trg=self:currentTarget() - if trg.target and trg.target._kind and trg.target._kind=="bitfield" then - trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] - else - --print(type(trg.target[trg.keys[trg.selected]]),trg.target[trg.keys[trg.selected]]._kind or "") - local trg_type=type(trg.target[trg.keys[trg.selected]]) - if trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected - self.mode=MODE_EDIT - self.input=tostring(trg.target[trg.keys[trg.selected]]) - elseif trg_type=='boolean' then - trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] - elseif trg_type=='userdata' then - self:pushTarget(trg.target[trg.keys[trg.selected]]) - --local screen = mkinstance(gui.FramedScreen,item_screen):init(trg.target[trg.keys[trg.selected]]) -- does not work - --screen:show() - else - print("Unknow type:"..trg_type) - print("Subtype:"..tostring(trg.target[trg.keys[trg.selected]]._kind)) - end - end - end, - cancelEdit = function(self) - self.mode=MODE_BROWSE - self.input="" - end, - commitEdit = function(self) - local trg=self:currentTarget() - self.mode=MODE_BROWSE - if type(trg.target[trg.keys[trg.selected]])=='number' then - trg.target[trg.keys[trg.selected]]=tonumber(self.input) - elseif type(trg.target[trg.keys[trg.selected]])=='string' then - trg.target[trg.keys[trg.selected]]=self.input - end - end, - onRenderBody = function(self, dc) - local trg=self:currentTarget() - dc:seek(2,1):string(tostring(trg.target), COLOR_RED) - local offset=2 - local page_offset=0 - local current_item=1 - local t_col - if math.floor(trg.selected / (self.frame_height-offset-2)) >0 then - page_offset=math.floor(trg.selected / (self.frame_height-offset-2))*(self.frame_height-offset-2)-1 - end - for k,v in pairs(trg.target) do - - if current_item==trg.selected then - t_col=COLOR_LIGHTGREEN - else - t_col=COLOR_GRAY - end - - if current_item-page_offset > 0 then - local y_pos=current_item-page_offset+offset - dc:seek(2,y_pos):string(tostring(k),t_col) - - if self.mode==MODE_EDIT and current_item==trg.selected then - dc:seek(20,y_pos):string(self.input..'_',COLOR_GREEN) - else - dc:seek(20,y_pos):string(tostring(v),t_col) - end - end - current_item=current_item+1 - end - end, - - onInput = function(self,keys) - if self.mode==MODE_BROWSE then - if keys.LEAVESCREEN then - self:popTarget() - elseif keys.CURSOR_UP then - self:changeSelected(-1) - elseif keys.CURSOR_DOWN then - self:changeSelected(1) - elseif keys.CURSOR_UP_FAST then - self:changeSelected(-10) - elseif keys.CURSOR_DOWN_FAST then - self:changeSelected(10) - elseif keys.SELECT then - self:editSelected() - elseif keys.CUSTOM_ALT_E then - --self:specialEditor() - local screen = mkinstance(TextInputDialog):init("Input new coordinates") - screen:show() - elseif keys.CUSTOM_ALT_I then --insert - self:insertNew() - elseif keys.CUSTOM_ALT_D then --delete - self:deleteSelected() - end - elseif self.mode==MODE_EDIT then - if keys.LEAVESCREEN then - self:cancelEdit() - elseif keys.SELECT then - self:commitEdit() - elseif keys._STRING then - if keys._STRING==0 then - self.input=string.sub(self.input,1,-2) - else - self.input=self.input.. string.char(keys._STRING) - end - end - end - end, - pushTarget=function(self,target_to_push) - local new_tbl={} - new_tbl.target=target_to_push - new_tbl.keys={} - new_tbl.selected=1 - for k,v in pairs(target_to_push) do - table.insert(new_tbl.keys,k) - end - new_tbl.item_count=#new_tbl.keys - table.insert(self.stack,new_tbl) - end, - popTarget=function(self) - table.remove(self.stack) --removes last element - if #self.stack==0 then - self:dismiss() - end - end, - init = function(self,item_to_edit) - self:pushTarget(item_to_edit) - self.frame_width,self.frame_height=dfhack.screen.getWindowSize() - return self - end } +function GmEditorUi:init(args) + self.stack={} + self.item_count=0 + self.mode=MODE_BROWSE + self.keys={} + self:pushTarget(args.target) + + return self +end +function GmEditorUi:insertNew(typename) + local tp=typename + if typename== nil then + dialog.showInputPrompt("Class type","Input class type:",COLOR_WHITE,"",dfhack.curry(self.insertNew,self)) + return + end + local ntype=df[tp] + if ntype== nil then + dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED) + return + end + + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="container" then + local thing=ntype:new() + dfhack.call_with_finalizer(1,false,df.delete,thing,trg.target.insert,trg.target,'#',thing) + + end +end +function GmEditorUi:deleteSelected() + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="container" then + trg.target:erase(trg.keys[trg.selected]) + end +end +function GmEditorUi:currentTarget() + return self.stack[#self.stack] +end +function GmEditorUi:changeSelected(delta) + local trg=self:currentTarget() + if trg.item_count <= 1 then return end + trg.selected = 1 + (trg.selected + delta - 1) % trg.item_count +end +function GmEditorUi:editSelected() + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="bitfield" then + trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] + else + --print(type(trg.target[trg.keys[trg.selected]]),trg.target[trg.keys[trg.selected]]._kind or "") + local trg_type=type(trg.target[trg.keys[trg.selected]]) + if trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected + self.mode=MODE_EDIT + self.input=tostring(trg.target[trg.keys[trg.selected]]) + elseif trg_type=='boolean' then + trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]] + elseif trg_type=='userdata' then + self:pushTarget(trg.target[trg.keys[trg.selected]]) + --local screen = mkinstance(gui.FramedScreen,GmEditorUi):init(trg.target[trg.keys[trg.selected]]) -- does not work + --screen:show() + else + print("Unknow type:"..trg_type) + print("Subtype:"..tostring(trg.target[trg.keys[trg.selected]]._kind)) + end + end +end +function GmEditorUi:cancelEdit() + self.mode=MODE_BROWSE + self.input="" +end +function GmEditorUi:commitEdit() + local trg=self:currentTarget() + self.mode=MODE_BROWSE + if type(trg.target[trg.keys[trg.selected]])=='number' then + trg.target[trg.keys[trg.selected]]=tonumber(self.input) + elseif type(trg.target[trg.keys[trg.selected]])=='string' then + trg.target[trg.keys[trg.selected]]=self.input + end +end +function GmEditorUi:onRenderBody( dc) + local trg=self:currentTarget() + dc:seek(2,1):string(tostring(trg.target), COLOR_RED) + local offset=2 + local page_offset=0 + local current_item=1 + local t_col + local width,height=self:getWindowSize() + local window_height=height-offset-2 + local cursor_window=math.floor(trg.selected / window_height) + if cursor_window>0 then + page_offset=cursor_window*window_height-1 + end + for k,v in pairs(trg.target) do + + if current_item==trg.selected then + t_col=COLOR_LIGHTGREEN + else + t_col=COLOR_GRAY + end + + if current_item-page_offset > 0 then + local y_pos=current_item-page_offset+offset + dc:seek(2,y_pos):string(tostring(k),t_col) + + if self.mode==MODE_EDIT and current_item==trg.selected then + dc:seek(20,y_pos):string(self.input..'_',COLOR_GREEN) + else + dc:seek(20,y_pos):string(tostring(v),t_col) + end + if y_pos+3>height then + break + end + end + current_item=current_item+1 + + end +end + function GmEditorUi:onInput(keys) + if self.mode==MODE_BROWSE then + if keys.LEAVESCREEN then + self:popTarget() + elseif keys.CURSOR_UP then + self:changeSelected(-1) + elseif keys.CURSOR_DOWN then + self:changeSelected(1) + elseif keys.CURSOR_UP_FAST then + self:changeSelected(-10) + elseif keys.CURSOR_DOWN_FAST then + self:changeSelected(10) + elseif keys.SELECT then + self:editSelected() + elseif keys.CUSTOM_ALT_E then + --self:specialEditor() + local screen = mkinstance(TextInputDialog):init("Input new coordinates") + screen:show() + elseif keys.CUSTOM_ALT_I then --insert + self:insertNew() + elseif keys.CUSTOM_ALT_D then --delete + self:deleteSelected() + end + elseif self.mode==MODE_EDIT then + if keys.LEAVESCREEN then + self:cancelEdit() + elseif keys.SELECT then + self:commitEdit() + elseif keys._STRING then + if keys._STRING==0 then + self.input=string.sub(self.input,1,-2) + else + self.input=self.input.. string.char(keys._STRING) + end + end + end +end +function GmEditorUi:pushTarget(target_to_push) + local new_tbl={} + new_tbl.target=target_to_push + new_tbl.keys={} + new_tbl.selected=1 + for k,v in pairs(target_to_push) do + table.insert(new_tbl.keys,k) + end + new_tbl.item_count=#new_tbl.keys + table.insert(self.stack,new_tbl) +end +function GmEditorUi:popTarget() + table.remove(self.stack) --removes last element + if #self.stack==0 then + self:dismiss() + end +end +local screen = GmEditorUi{target=my_trg} - -local screen = mkinstance(gui.FramedScreen,item_screen):init(my_trg) screen:show() \ No newline at end of file From 39df1e0eceef0e6eafcb47f6274b26557f02ef61 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Sep 2012 23:23:12 +0300 Subject: [PATCH 17/79] Removed unused stuff from editor --- scripts/gui/gm-editor.lua | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index 44345b3e8..a86a9f4a8 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -25,18 +25,6 @@ else qerror("No valid target found") end -TextInputDialog = defclass(TextInputDialog, gui.FramedScreen) - -function TextInputDialog:init(prompt) - self.frame_style=GREY_LINE_FRAME - self.frame_title=prompt - self.input="" - return self -end -function TextInputDialog:onRenderBody(dc) - dc:seek(1,1):string(self.input, COLOR_WHITE):newline() -end - local MODE_BROWSE=0 local MODE_EDIT=1 GmEditorUi = defclass(GmEditorUi, gui.FramedScreen) @@ -175,8 +163,6 @@ end self:editSelected() elseif keys.CUSTOM_ALT_E then --self:specialEditor() - local screen = mkinstance(TextInputDialog):init("Input new coordinates") - screen:show() elseif keys.CUSTOM_ALT_I then --insert self:insertNew() elseif keys.CUSTOM_ALT_D then --delete From 28354715fff082194499fabe7a02a37b72a53d71 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 23 Sep 2012 23:45:19 +0300 Subject: [PATCH 18/79] Editor with dialog mode (no without switching from/to console to edit anything!) --- scripts/gui/gm-editor.lua | 67 +++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index a86a9f4a8..309663bdf 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -1,30 +1,34 @@ -- Interface powered item editor. local gui = require 'gui' local dialog = require 'gui.dialogs' +local args={...} +function getTargetFromScreens() + local my_trg + if dfhack.gui.getCurFocus() == 'item' then + my_trg=dfhack.gui.getCurViewscreen().item + elseif dfhack.gui.getCurFocus() == 'joblist' then + local t_screen=dfhack.gui.getCurViewscreen() + my_trg=t_screen.jobs[t_screen.cursor_pos] + elseif dfhack.gui.getCurFocus() == 'createquota' then + local t_screen=dfhack.gui.getCurViewscreen() + my_trg=t_screen.orders[t_screen.sel_idx] + elseif dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Flow' then + local t_look=df.global.ui_look_list.items[df.global.ui_look_cursor] + my_trg=t_look.flow -local my_trg -if dfhack.gui.getCurFocus() == 'item' then - my_trg=dfhack.gui.getCurViewscreen().item -elseif dfhack.gui.getCurFocus() == 'joblist' then - local t_screen=dfhack.gui.getCurViewscreen() - my_trg=t_screen.jobs[t_screen.cursor_pos] -elseif dfhack.gui.getCurFocus() == 'createquota' then - local t_screen=dfhack.gui.getCurViewscreen() - my_trg=t_screen.orders[t_screen.sel_idx] -elseif dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Flow' then - local t_look=df.global.ui_look_list.items[df.global.ui_look_cursor] - my_trg=t_look.flow - -elseif dfhack.gui.getSelectedUnit(true) then - my_trg=dfhack.gui.getSelectedUnit(true) -elseif dfhack.gui.getSelectedItem(true) then - my_trg=dfhack.gui.getSelectedItem(true) -elseif dfhack.gui.getSelectedJob(true) then - my_trg=dfhack.gui.getSelectedJob(true) -else - qerror("No valid target found") + elseif dfhack.gui.getSelectedUnit(true) then + my_trg=dfhack.gui.getSelectedUnit(true) + elseif dfhack.gui.getSelectedItem(true) then + my_trg=dfhack.gui.getSelectedItem(true) + elseif dfhack.gui.getSelectedJob(true) then + my_trg=dfhack.gui.getSelectedJob(true) + else + qerror("No valid target found") + end + return my_trg end + local MODE_BROWSE=0 local MODE_EDIT=1 GmEditorUi = defclass(GmEditorUi, gui.FramedScreen) @@ -199,7 +203,22 @@ function GmEditorUi:popTarget() self:dismiss() end end +function show_editor(trg) + local screen = GmEditorUi{target=trg} + screen:show() +end +if #args~=0 then + if args[1]=="dialog" then + function thunk(entry) + local t=load("return "..entry)() + show_editor(t) + end + dialog.showInputPrompt("Gm Editor", "Object to edit:", COLOR_GRAY, "",thunk) + else + local t=load("return "..args[1])() + show_editor(t) + end +else + show_editor(getTargetFromScreens()) +end -local screen = GmEditorUi{target=my_trg} - -screen:show() \ No newline at end of file From bd2f3a9998c72d85d663da44fbdb30702b3e0c6d Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Sep 2012 00:24:37 +0300 Subject: [PATCH 19/79] Moved lua out of dfusion. Now lua is a script. supports --file (or -f) flag, usage: lua or lua --file or just "lua" for interactive interpreter. --- plugins/Dfusion/dfusion.cpp | 62 ------------------------------------- scripts/lua.lua | 10 ++++++ 2 files changed, 10 insertions(+), 62 deletions(-) create mode 100644 scripts/lua.lua diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index 78c3fa8d1..6c698f58a 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -33,8 +33,6 @@ DFHACK_PLUGIN("dfusion") command_result dfusion (color_ostream &out, std::vector ¶meters); command_result dfuse (color_ostream &out, std::vector ¶meters); -command_result lua_run (color_ostream &out, std::vector ¶meters); -command_result lua_run_file (color_ostream &out, std::vector ¶meters); DFhackCExport const char * plugin_name ( void ) { @@ -65,8 +63,6 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector ' to run instead.",lua_run,true)); - commands.push_back(PluginCommand("runlua", "Run non-interactive interpreter. Use 'runlua ' to run .",lua_run_file,false)); mymutex=new tthread::mutex; return CR_OK; } @@ -107,64 +103,6 @@ DFhackCExport command_result plugin_onupdate_DISABLED ( Core * c ) mymutex->unlock(); return CR_OK; } -command_result lua_run_file (color_ostream &out, std::vector ¶meters) -{ - if(parameters.size()==0) - { - out.printerr("runlua without file to run!"); - return CR_FAILURE; - } - return lua_run(out,parameters); -} -command_result lua_run (color_ostream &out, std::vector ¶meters) -{ - if (!parameters.empty()) - { - if (parameters[0] == "--core-context") - { - Lua::InterpreterLoop(out, Lua::Core::State, "core lua"); - return CR_OK; - } - else if (parameters[0] == "--core-reload") - { - CoreSuspender suspend; - - for (size_t i = 1; i < parameters.size(); i++) - { - lua_getglobal(Lua::Core::State, "reload"); - lua_pushstring(Lua::Core::State, parameters[i].c_str()); - Lua::SafeCall(out, Lua::Core::State, 1, 0); - } - - return CR_OK; - } - } - - mymutex->lock(); - lua::state s=lua::glua::Get(); - - if(parameters.size()>0) - { - try{ - s.loadfile(parameters[0]); //load file - for(size_t i=1;iunlock(); - return CR_OK; -} void RunDfusion(color_ostream &out, std::vector ¶meters) { mymutex->lock(); diff --git a/scripts/lua.lua b/scripts/lua.lua new file mode 100644 index 000000000..c2033b7e6 --- /dev/null +++ b/scripts/lua.lua @@ -0,0 +1,10 @@ +local args={...} +if args[1]=="--file" or args[1]=="-f" then + local f=loadfile (args[2]) + dfhack.pcall(f,table.unpack(args,3)) +elseif args[1]~=nil then + local f=load(args[1],'=(lua command)', 't',) + dfhack.pcall(f,table.unpack(args,2)) +else + dfhack.interpreter("lua","lua.history") +end \ No newline at end of file From cc5df57e53bf89b8cce51353c0a8b4f048d23679 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Sep 2012 10:24:45 +0300 Subject: [PATCH 20/79] Little error fixed in lua script --- scripts/lua.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lua.lua b/scripts/lua.lua index c2033b7e6..497498e86 100644 --- a/scripts/lua.lua +++ b/scripts/lua.lua @@ -3,7 +3,7 @@ if args[1]=="--file" or args[1]=="-f" then local f=loadfile (args[2]) dfhack.pcall(f,table.unpack(args,3)) elseif args[1]~=nil then - local f=load(args[1],'=(lua command)', 't',) + local f=load(args[1],'=(lua command)', 't') dfhack.pcall(f,table.unpack(args,2)) else dfhack.interpreter("lua","lua.history") From 0bee8c360e1ad76ea68352e5d1806f79f910cd66 Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Sep 2012 10:25:47 +0300 Subject: [PATCH 21/79] Reaction hooks experimentation. --- plugins/CMakeLists.txt | 1 + plugins/lua/reactionhooks.lua | 13 ++ plugins/reactionhooks.cpp | 319 ++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 plugins/lua/reactionhooks.lua create mode 100644 plugins/reactionhooks.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 0b0ad0461..4d4f7493f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -123,6 +123,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(steam-engine steam-engine.cpp) DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(siege-engine siege-engine.cpp LINK_LIBRARIES lua) + DFHACK_PLUGIN(reactionhooks reactionhooks.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(add-spatter add-spatter.cpp) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) diff --git a/plugins/lua/reactionhooks.lua b/plugins/lua/reactionhooks.lua new file mode 100644 index 000000000..5f3622e2f --- /dev/null +++ b/plugins/lua/reactionhooks.lua @@ -0,0 +1,13 @@ +local _ENV = mkmodule('plugins.reactionhooks') + +--[[ + + Native events: + + * onReactionComplete(burrow) + +--]] + +rawset_default(_ENV, dfhack.reactionhooks) + +return _ENV \ No newline at end of file diff --git a/plugins/reactionhooks.cpp b/plugins/reactionhooks.cpp new file mode 100644 index 000000000..4041b99a5 --- /dev/null +++ b/plugins/reactionhooks.cpp @@ -0,0 +1,319 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "df/item_liquid_miscst.h" +#include "df/item_constructed.h" +#include "df/builtin_mats.h" +#include "df/world.h" +#include "df/job.h" +#include "df/job_item.h" +#include "df/job_item_ref.h" +#include "df/ui.h" +#include "df/report.h" +#include "df/reaction.h" +#include "df/reaction_reagent_itemst.h" +#include "df/reaction_product_itemst.h" +#include "df/matter_state.h" +#include "df/contaminant.h" + +#include "MiscUtils.h" +#include "LuaTools.h" + +using std::vector; +using std::string; +using std::stack; +using namespace DFHack; +using namespace df::enums; + +using df::global::gps; +using df::global::world; +using df::global::ui; + +typedef df::reaction_product_itemst item_product; + +DFHACK_PLUGIN("reactionhooks"); + +struct ReagentSource { + int idx; + df::reaction_reagent *reagent; + + ReagentSource() : idx(-1), reagent(NULL) {} +}; + +struct MaterialSource : ReagentSource { + bool product; + std::string product_name; + + int mat_type, mat_index; + + MaterialSource() : product(false), mat_type(-1), mat_index(-1) {} +}; + +struct ProductInfo { + df::reaction *react; + item_product *product; + + MaterialSource material; + + bool isValid() { + return (material.mat_type >= 0 || material.reagent); + } +}; + +struct ReactionInfo { + df::reaction *react; + + std::vector products; +}; + +static std::map reactions; +static std::map products; + +static ReactionInfo *find_reaction(const std::string &name) +{ + auto it = reactions.find(name); + return (it != reactions.end()) ? &it->second : NULL; +} + +static bool is_lua_hook(const std::string &name) +{ + return name.size() > 9 && memcmp(name.data(), "LUA_HOOK_", 9) == 0; +} + +static void find_material(int *type, int *index, df::item *input, MaterialSource &mat) +{ + if (input && mat.reagent) + { + MaterialInfo info(input); + + if (mat.product) + { + if (!info.findProduct(info, mat.product_name)) + { + color_ostream_proxy out(Core::getInstance().getConsole()); + out.printerr("Cannot find product '%s'\n", mat.product_name.c_str()); + } + } + + *type = info.type; + *index = info.index; + } + else + { + *type = mat.mat_type; + *index = mat.mat_index; + } +} + + +/* + * Hooks + */ + +typedef std::map > item_table; + +static void index_items(item_table &table, df::job *job, ReactionInfo *info) +{ + for (int i = job->items.size()-1; i >= 0; i--) + { + auto iref = job->items[i]; + if (iref->job_item_idx < 0) continue; + auto iitem = job->job_items[iref->job_item_idx]; + + if (iitem->contains.empty()) + { + table[iitem->reagent_index].push_back(iref->item); + } + else + { + std::vector contents; + Items::getContainedItems(iref->item, &contents); + + for (int j = contents.size()-1; j >= 0; j--) + { + for (int k = iitem->contains.size()-1; k >= 0; k--) + { + int ridx = iitem->contains[k]; + auto reag = info->react->reagents[ridx]; + + if (reag->matchesChild(contents[j], info->react, iitem->reaction_id)) + table[ridx].push_back(contents[j]); + } + } + } + } +} + +df::item* find_item(ReagentSource &info, item_table &table) +{ + if (!info.reagent) + return NULL; + if (table[info.idx].empty()) + return NULL; + return table[info.idx].back(); +} + + + +df::item* find_item( + ReagentSource &info, + std::vector *in_reag, + std::vector *in_items +) { + if (!info.reagent) + return NULL; + for (int i = in_items->size(); i >= 0; i--) + if ((*in_reag)[i] == info.reagent) + return (*in_items)[i]; + return NULL; +} + +static void handle_reaction_done(color_ostream &out, df::unit *unit, std::vector *in_items, std::vector *out_items,bool *call_native){}; + +DEFINE_LUA_EVENT_4(onReactionComplete, handle_reaction_done, df::unit *, std::vector *,std::vector *,bool *); + + +DFHACK_PLUGIN_LUA_EVENTS { + DFHACK_LUA_EVENT(onReactionComplete), + DFHACK_LUA_END +}; + +struct product_hook : item_product { + typedef item_product interpose_base; + + DEFINE_VMETHOD_INTERPOSE( + void, produce, + (df::unit *unit, std::vector *out_items, + std::vector *in_reag, + std::vector *in_items, + int32_t quantity, int16_t skill, + df::historical_entity *entity, df::world_site *site) + ) { + if (auto product = products[this]) + { + color_ostream_proxy out(Core::getInstance().getConsole()); + bool call_native=true; + onReactionComplete(out,unit,in_items,out_items,&call_native); + if(!call_native) + return; + } + + INTERPOSE_NEXT(produce)(unit, out_items, in_reag, in_items, quantity, skill, entity, site); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(product_hook, produce); + + + + + +/* + * Scan raws for matching reactions. + */ + + +static void parse_product( + color_ostream &out, ProductInfo &info, df::reaction *react, item_product *prod + ) { + info.react = react; + info.product = prod; + info.material.mat_type = prod->mat_type; + info.material.mat_index = prod->mat_index; +} + +static bool find_reactions(color_ostream &out) +{ + reactions.clear(); + + auto &rlist = world->raws.reactions; + + for (size_t i = 0; i < rlist.size(); i++) + { + if (!is_lua_hook(rlist[i]->code)) + continue; + reactions[rlist[i]->code].react = rlist[i]; + } + + for (auto it = reactions.begin(); it != reactions.end(); ++it) + { + auto &prod = it->second.react->products; + auto &out_prod = it->second.products; + + for (size_t i = 0; i < prod.size(); i++) + { + auto itprod = strict_virtual_cast(prod[i]); + if (!itprod) continue; + + out_prod.push_back(ProductInfo()); + parse_product(out, out_prod.back(), it->second.react, itprod); + } + + for (size_t i = 0; i < prod.size(); i++) + { + if (out_prod[i].isValid()) + products[out_prod[i].product] = &out_prod[i]; + } + } + + return !products.empty(); +} + +static void enable_hooks(bool enable) +{ + INTERPOSE_HOOK(product_hook, produce).apply(enable); +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_WORLD_LOADED: + if (find_reactions(out)) + { + out.print("Detected reaction hooks - enabling plugin.\n"); + enable_hooks(true); + } + else + enable_hooks(false); + break; + case SC_WORLD_UNLOADED: + enable_hooks(false); + reactions.clear(); + products.clear(); + break; + default: + break; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + if (Core::getInstance().isWorldLoaded()) + plugin_onstatechange(out, SC_WORLD_LOADED); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + enable_hooks(false); + return CR_OK; +} From ddc83a0a72edb7c23abe8f08daa4750ead3dc5ea Mon Sep 17 00:00:00 2001 From: Warmist Date: Tue, 25 Sep 2012 11:30:38 +0300 Subject: [PATCH 22/79] Another dfusion nuking (not much left :) ) --- plugins/Dfusion/dfusion.cpp | 11 +- plugins/Dfusion/include/functioncall.h | 26 --- plugins/Dfusion/include/lua_FunctionCall.h | 14 -- plugins/Dfusion/include/lua_Misc.h | 1 - plugins/Dfusion/include/lua_Offsets.h | 13 -- plugins/Dfusion/include/lua_VersionInfo.h | 12 -- plugins/Dfusion/src/functioncall.cpp | 121 ------------- plugins/Dfusion/src/lua_FunctionCall.cpp | 39 ----- plugins/Dfusion/src/lua_Misc.cpp | 99 +---------- plugins/Dfusion/src/lua_Offsets.cpp | 190 --------------------- plugins/Dfusion/src/lua_VersionInfo.cpp | 115 ------------- 11 files changed, 5 insertions(+), 636 deletions(-) delete mode 100644 plugins/Dfusion/include/functioncall.h delete mode 100644 plugins/Dfusion/include/lua_FunctionCall.h delete mode 100644 plugins/Dfusion/include/lua_Offsets.h delete mode 100644 plugins/Dfusion/include/lua_VersionInfo.h delete mode 100644 plugins/Dfusion/src/functioncall.cpp delete mode 100644 plugins/Dfusion/src/lua_FunctionCall.cpp delete mode 100644 plugins/Dfusion/src/lua_Offsets.cpp delete mode 100644 plugins/Dfusion/src/lua_VersionInfo.cpp diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index 6c698f58a..0f49e860d 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -14,10 +14,8 @@ #include "lua_Process.h" #include "lua_Hexsearch.h" #include "lua_Misc.h" -#include "lua_VersionInfo.h" -#include "functioncall.h" -#include "lua_FunctionCall.h" -#include "lua_Offsets.h" + + #include "DataDefs.h" #include "LuaTools.h" @@ -43,15 +41,12 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector -using std::vector; -using std::size_t; -class FunctionCaller -{ -public: - enum callconv - { - STD_CALL, //__stdcall - all in stack - FAST_CALL, //__fastcall - as much in registers as fits - THIS_CALL, //__thiscall - eax ptr to this, rest in stack - CDECL_CALL //__cdecl - same as stdcall but no stack realign - }; - - FunctionCaller(size_t base):base_(base){}; - - int CallFunction(size_t func_ptr,callconv conv,const vector &arguments); - -private: - int CallF(size_t count,callconv conv,void* f,const vector &arguments); - size_t base_; -}; - -#endif //FUNCTIONCALL__H \ No newline at end of file diff --git a/plugins/Dfusion/include/lua_FunctionCall.h b/plugins/Dfusion/include/lua_FunctionCall.h deleted file mode 100644 index bc1af5685..000000000 --- a/plugins/Dfusion/include/lua_FunctionCall.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef LUA_FUNCTIONCALL__H -#define LUA_FUNCTIONCALL__H - -#include "luamain.h" -#include "functioncall.h" - -namespace lua -{ - - -void RegisterFunctionCall(lua::state &st); - -} -#endif \ No newline at end of file diff --git a/plugins/Dfusion/include/lua_Misc.h b/plugins/Dfusion/include/lua_Misc.h index b39d60214..ff4611456 100644 --- a/plugins/Dfusion/include/lua_Misc.h +++ b/plugins/Dfusion/include/lua_Misc.h @@ -7,7 +7,6 @@ #include #include "luamain.h" #include "OutFile.h" -#include "functioncall.h" #include "LuaTools.h" namespace lua diff --git a/plugins/Dfusion/include/lua_Offsets.h b/plugins/Dfusion/include/lua_Offsets.h deleted file mode 100644 index 8d6a94b95..000000000 --- a/plugins/Dfusion/include/lua_Offsets.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef LUA_OFFSETS_H -#define LUA_OFFSETS_H -#include "luamain.h" - -namespace lua -{ - -void RegisterEngine(lua::state &st); - -} - - -#endif \ No newline at end of file diff --git a/plugins/Dfusion/include/lua_VersionInfo.h b/plugins/Dfusion/include/lua_VersionInfo.h deleted file mode 100644 index a4d7ed658..000000000 --- a/plugins/Dfusion/include/lua_VersionInfo.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef LUA_VERSIONINFO_H -#define LUA_VERSIONINFO_H -#include "Core.h" -#include -#include "luamain.h" -namespace lua -{ - -void RegisterVersionInfo(lua::state &st); - -} -#endif diff --git a/plugins/Dfusion/src/functioncall.cpp b/plugins/Dfusion/src/functioncall.cpp deleted file mode 100644 index b6c38c928..000000000 --- a/plugins/Dfusion/src/functioncall.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "functioncall.h" - -#ifdef LINUX_BUILD -#define __F_TDEF(CONV,CONV_,tag) typedef int(__attribute__ ( (CONV_) ) *F_TYPE##CONV##tag) -#else -#define __F_TDEF(CONV,CONV_,tag) typedef int(__ ## CONV_ *F_TYPE##CONV##tag) -#endif - -#define __F_T(CONV,tag) F_TYPE##CONV##tag -#define __F_TYPEDEFS(CONV,CONV_) __F_TDEF(CONV,CONV_,1)(int);\ - __F_TDEF(CONV,CONV_,2)(int,int);\ - __F_TDEF(CONV,CONV_,3)(int,int,int);\ - __F_TDEF(CONV,CONV_,4)(int,int,int,int);\ - __F_TDEF(CONV,CONV_,5)(int,int,int,int,int);\ - __F_TDEF(CONV,CONV_,6)(int,int,int,int,int,int);\ - __F_TDEF(CONV,CONV_,7)(int,int,int,int,int,int,int) - - -#define __FCALL(CONV,CONV_) if(conv==CONV)\ - { \ - if(count==1)\ - ret= (reinterpret_cast<__F_T(CONV,1)>(f))\ - (arguments[0]);\ - else if(count==2)\ - ret= (reinterpret_cast<__F_T(CONV,2)>(f))\ - (arguments[0],arguments[1]);\ - else if(count==3)\ - ret= (reinterpret_cast<__F_T(CONV,3)>(f))\ - (arguments[0],arguments[1],arguments[2]);\ - else if(count==4)\ - ret= (reinterpret_cast<__F_T(CONV,4)>(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3]);\ - else if(count==5)\ - ret= (reinterpret_cast<__F_T(CONV,5)>(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);\ - else if(count==6)\ - ret= (reinterpret_cast<__F_T(CONV,6)>(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);\ - else if(count==7)\ - ret= (reinterpret_cast<__F_T(CONV,7)>(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6]);\ - } - -#define __FCALLEX(CONV,CONV_) if(conv==CONV)\ - { if(count==1) {__F_T(CONV,1) tmp_F=reinterpret_cast<__F_T(CONV,1)>(f); return tmp_F(arguments[0]);}\ - else if(count==2){__F_T(CONV,2) tmp_F=reinterpret_cast<__F_T(CONV,2)>(f); return tmp_F(arguments[0],arguments[1]);}\ - else if(count==3){__F_T(CONV,3) tmp_F=reinterpret_cast<__F_T(CONV,3)>(f); return tmp_F(arguments[0],arguments[1],arguments[2]);}\ - else if(count==4){__F_T(CONV,4) tmp_F=reinterpret_cast<__F_T(CONV,4)>(f); return tmp_F(arguments[0],arguments[1],arguments[2],arguments[3]);}\ - else if(count==5){__F_T(CONV,5) tmp_F=reinterpret_cast<__F_T(CONV,5)>(f); return tmp_F(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4]);}\ - else if(count==6){__F_T(CONV,6) tmp_F=reinterpret_cast<__F_T(CONV,6)>(f); return tmp_F(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);}\ - else if(count==7){__F_T(CONV,7) tmp_F=reinterpret_cast<__F_T(CONV,7)>(f); return tmp_F(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6]);}\ - } - /*else if(count==8)\ - ret= (reinterpret_cast<__F_T(CONV,8)>(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7]);\ - else if(count==9)\ - ret= (reinterpret_cast(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8]);\ - else if(count==10)\ - ret= (reinterpret_cast(f))\ - (arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9]);}*/ - - -/*int FunctionCaller::CallF(size_t count,callconv conv,void* f,const vector &arguments)//more complex but not more error safe -{ - __F_TYPEDEFS(STD_CALL,__stdcall); - __F_TYPEDEFS(FAST_CALL,__fastcall); - __F_TYPEDEFS(THIS_CALL,__thiscall); - __F_TYPEDEFS(CDECL_CALL,__cdecl); - { - __FCALLEX(STD_CALL,__stdcall); - __FCALLEX(FAST_CALL,__fastcall); - __FCALLEX(THIS_CALL,__thiscall); - __FCALLEX(CDECL_CALL,__cdecl); - } -}*/ -int FunctionCaller::CallFunction(size_t func_ptr,callconv conv,const vector &arguments) -{ - //nasty nasty code... -#ifdef LINUX_BUILD //quick fix - if(conv==THIS_CALL) - conv=STD_CALL; -#endif - void* f= reinterpret_cast(func_ptr+base_); - size_t count=arguments.size(); - if(count==0) - return (reinterpret_cast(f))(); //does not matter how we call it... - int ret=0; - //typedefs - __F_TYPEDEFS(STD_CALL,stdcall); - __F_TYPEDEFS(FAST_CALL,fastcall); - __F_TYPEDEFS(THIS_CALL,thiscall); - __F_TYPEDEFS(CDECL_CALL,cdecl); - //calls - __FCALL(STD_CALL,stdcall); - __FCALL(FAST_CALL,fastcall); - __FCALL(THIS_CALL,thiscall); - __FCALL(CDECL_CALL,cdecl); - return -1; //incorect type. Should probably throw... - //return CallF(count,conv,f,arguments); - /*//testing part{ worked some time ago..., put where DFHack::Core is accesible - c->Suspend(); - FunctionCaller caller(c->p->getBase()); - std::vector args; - args.push_back((size_t)"Hello world"); - args.push_back(4); - args.push_back(4); - args.push_back(0); - dfprint mprint=(dfprint)(0x27F030+c->p->getBase()); - mprint("Hello world",4,4,0); - //caller.CallFunction((0x27F030),FunctionCaller::THIS_CALL,args); - c->Resume(); - return CR_OK; - //}end testing*/ -} -#undef __FCALL -#undef __FCALLEX -#undef __F_TYPEDEFS -#undef __F_T diff --git a/plugins/Dfusion/src/lua_FunctionCall.cpp b/plugins/Dfusion/src/lua_FunctionCall.cpp deleted file mode 100644 index a537edbb5..000000000 --- a/plugins/Dfusion/src/lua_FunctionCall.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "lua_FunctionCall.h" -using namespace lua; -int lua_FunctionCall(lua_State *L) -{ - lua::state st(L); - FunctionCaller cl(0); - size_t ptr=st.as(1); - int callconv=st.as(2); - vector arguments; - for(int i=3;i(i)); - int ret=cl.CallFunction(ptr,(FunctionCaller::callconv)callconv,arguments); - st.push(ret); - return 1;// dunno if len is needed... -} - -const luaL_Reg lua_functioncall_func[]= -{ - {"call",lua_FunctionCall}, - {NULL,NULL} -}; - -#define __ADDCONST(name) st.push(::FunctionCaller:: name); st.setglobal(#name) -void lua::RegisterFunctionCall(lua::state &st) -{ - st.getglobal("FunctionCall"); - if(st.is()) - { - st.pop(); - st.newtable(); - } - __ADDCONST(STD_CALL); - __ADDCONST(FAST_CALL); - __ADDCONST(THIS_CALL); - __ADDCONST(CDECL_CALL); - lua::RegFunctionsLocal(st, lua_functioncall_func); - st.setglobal("FunctionCall"); -} -#undef __ADDCONST \ No newline at end of file diff --git a/plugins/Dfusion/src/lua_Misc.cpp b/plugins/Dfusion/src/lua_Misc.cpp index b58efc7ac..6a06781fd 100644 --- a/plugins/Dfusion/src/lua_Misc.cpp +++ b/plugins/Dfusion/src/lua_Misc.cpp @@ -129,111 +129,16 @@ static int GetMod(lua_State *L) st.push(pos); return 1; } -static int lua_malloc(lua_State *L) -{ - lua::state st(L); - size_t size=st.as(1); - size_t pos=reinterpret_cast(malloc(size)); - st.push(pos); - return 1; -} -static int lua_malloc_free(lua_State *L) -{ - lua::state st(L); - size_t ptr=st.as(1); - free(reinterpret_cast(ptr)); - return 0; -} -#ifdef LINUX_BUILD -static size_t __attribute__((stdcall)) PushValue(size_t ret,uint32_t eax,uint32_t ebx,uint32_t ecx,uint32_t edx,uint32_t edi,uint32_t esi,uint32_t esp,uint32_t ebp) -#else -static size_t __stdcall PushValue(size_t ret,uint32_t eax,uint32_t ebx,uint32_t ecx,uint32_t edx,uint32_t edi,uint32_t esi,uint32_t esp,uint32_t ebp) -#endif -{ - lua::state st=lua::glua::Get(); - st.getglobal("OnFunction"); - if(st.is()) - return 0; - st.newtable(); - st.push(eax); - st.setfield("eax"); - st.push(ebx); - st.setfield("ebx"); - st.push(ecx); - st.setfield("ecx"); - st.push(edx); - st.setfield("edx"); - st.push(edi); - st.setfield("edi"); - st.push(esi); - st.setfield("esi"); - st.push(esp+12); - st.setfield("esp"); - st.push(ebp); - st.setfield("ebp"); - st.push(ret); - st.setfield("ret"); - DFHack::Lua::SafeCall(DFHack::Core::getInstance().getConsole(),st,1,1); - return st.as(); -} -static int Get_PushValue(lua_State *L) -{ - lua::state st(L); - st.push((uint32_t)&PushValue); - return 1; -} -static int Call_Df(lua_State *L) -{ - lua::state st(L); - FunctionCaller f(0); - std::vector args; - size_t ptr; - FunctionCaller::callconv conv; - ptr=st.as(1); - conv=(FunctionCaller::callconv)st.as(2); - for(size_t j=3;j<=st.gettop();j++) - args.push_back(st.as(j)); - st.push(f.CallFunction(ptr,conv,args)); - return 1; -} -static int Suspend_Df(lua_State *L) -{ - lua::state st(L); - DFHack::Core::getInstance().Suspend(); - return 0; -} -static int Resume_Df(lua_State *L) -{ - lua::state st(L); - DFHack::Core::getInstance().Resume(); - return 0; -} -static int Cast(lua_State *L) -{ - lua::state st(L); - if(DFHack::Lua::IsDFObject(st,1)!=DFHack::Lua::OBJ_TYPE) - st.error("First argument must be df type!"); - if(!st.is(2)) //todo maybe lightuserdata? - st.error("Second argument must be pointer as a number!"); - st.getfield("_identity",1); - DFHack::Lua::PushDFObject(st,(DFHack::type_identity*)lua_touserdata(st,-1),(void*)st.as(2)); - return 1; -} + + const luaL_Reg lua_misc_func[]= { - {"alloc",lua_malloc}, - {"free",lua_malloc_free}, {"loadmod",LoadMod}, {"getmod",GetMod}, {"loadobj",LoadObj}, {"loadobjsymbols",LoadObjSymbols}, {"findmarker",FindMarker}, {"newmod",NewMod}, - {"getpushvalue",Get_PushValue}, - {"calldf",Call_Df}, - {"suspend",Suspend_Df}, - {"resume",Resume_Df}, - {"cast",Cast}, {NULL,NULL} }; void lua::RegisterMisc(lua::state &st) diff --git a/plugins/Dfusion/src/lua_Offsets.cpp b/plugins/Dfusion/src/lua_Offsets.cpp deleted file mode 100644 index 4d66e67e7..000000000 --- a/plugins/Dfusion/src/lua_Offsets.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "lua_Offsets.h" -#include -#include -//TODO make a seperate module with peeks/pokes and page permisions (linux/windows spec) -//TODO maybe remove alltogether- use DFHack::Process instead? -template -T engine_peek(size_t offset) -{ - return *(reinterpret_cast(offset)); -} -template -void engine_poke(size_t offset,T val) -{ - *(reinterpret_cast(offset))=val; -} - -void peekarb(size_t offset, void *mem,size_t size) -{ - memcpy(mem,(void*)offset,size); -} -void peekstr(size_t offset, char* buf, size_t maxsize) -{ - strncpy(buf,(char*)offset,maxsize); -} -void pokearb(size_t offset, void *mem,size_t size) -{ - memcpy((void*)offset,mem,size); -} -void pokestr(size_t offset, char* buf, size_t maxsize) -{ - strncpy((char*)offset,buf,maxsize); -} - -//// lua stuff here -static int lua_peekb(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekw(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekd(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekq(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekfloat(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekdouble(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_peekarb(lua_State *L) -{ - lua::state st(L); - size_t size=st.as(2); - void *p=st.newuserdata(size); - peekarb(st.as(1),p,size); - return 1; -} -static int lua_peekstr(lua_State *L) -{ - lua::state st(L); - char *buf; - buf=new char[256]; - peekstr(st.as(1),buf,256); - std::string tstr(buf); - st.push(tstr); - delete [] buf; - return 1; -} -static int lua_peekstr2(lua_State *L) -{ - lua::state st(L); - st.push(engine_peek(st.as(1))); - return 1; -} -static int lua_pokeb(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_pokew(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_poked(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_pokeq(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_pokefloat(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_pokedouble(lua_State *L) -{ - lua::state st(L); - engine_poke(st.as(1),st.as(2)); - return 0; -} -static int lua_pokearb(lua_State *L) -{ - lua::state st(L); - void *p=(void *)lua_touserdata(L, 2);//st.as(2); - size_t size=st.as(3); - pokearb(st.as(1),p,size); - return 0; -} -static int lua_pokestr(lua_State *L) -{ - lua::state st(L); - std::string trg=st.as(2); - pokestr(st.as(1),(char*)trg.c_str(),trg.size()); - return 0; -} -static int lua_pokestr2(lua_State *L) -{ - lua::state st(L); - std::string trg=st.as(2); - engine_poke(st.as(1),trg); - return 0; -} -const luaL_Reg lua_engine_func[]= -{ - {"peekb",lua_peekb}, - {"peekw",lua_peekw}, - {"peekd",lua_peekd}, - {"peekq",lua_peekq}, - {"peekfloat",lua_peekfloat}, - {"peekdouble",lua_peekdouble}, - - {"peekarb",lua_peekarb}, - {"peekstr",lua_peekstr}, - {"peekstr2",lua_peekstr2}, - - {"pokeb",lua_pokeb}, - {"pokew",lua_pokew}, - {"poked",lua_poked}, - {"pokeq",lua_pokeq}, - {"pokefloat",lua_pokefloat}, - {"pokedouble",lua_pokedouble}, - - {"pokearb",lua_pokearb}, - {"pokestr",lua_pokestr}, - {"pokestr2",lua_pokestr2}, - {NULL,NULL} -}; - -void lua::RegisterEngine(lua::state &st) -{ - st.getglobal("engine"); - if(st.is()) - { - st.pop(); - st.newtable(); - } - lua::RegFunctionsLocal(st,lua_engine_func); - st.setglobal("engine"); -} diff --git a/plugins/Dfusion/src/lua_VersionInfo.cpp b/plugins/Dfusion/src/lua_VersionInfo.cpp deleted file mode 100644 index 9f5cd17e7..000000000 --- a/plugins/Dfusion/src/lua_VersionInfo.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "lua_VersionInfo.h" - -#define VI_FUNC(name) int lua_VI_ ## name (lua_State *L){\ - lua::state s(L);\ - DFHack::VersionInfo* vif=DFHack::Core::getInstance().vinfo; -#define END_VI_FUNC } - -VI_FUNC(getBase) -//int lua_VI_getBase(lua_State *L) - s.push(vif->getBase()); - return 1; -END_VI_FUNC - -VI_FUNC(setBase) - uint32_t val=s.as(1); - vif->setBase(val); - return 0; -END_VI_FUNC -VI_FUNC(rebaseTo) - uint32_t val=s.as(1); - vif->rebaseTo(val); - return 0; -END_VI_FUNC -VI_FUNC(addMD5) - std::string val=s.as(1); - vif->addMD5(val); - return 0; -END_VI_FUNC -VI_FUNC(hasMD5) - std::string val=s.as(1); - s.push(vif->hasMD5(val)); - return 1; -END_VI_FUNC - -VI_FUNC(addPE) - uint32_t val=s.as(1); - vif->addPE(val); - return 0; -END_VI_FUNC - -VI_FUNC(hasPE) - uint32_t val=s.as(1); - s.push(vif->hasPE(val)); - return 1; -END_VI_FUNC - -VI_FUNC(setVersion) - std::string val=s.as(1); - vif->setVersion(val); - return 0; -END_VI_FUNC - -VI_FUNC(getVersion) - s.push(vif->getVersion()); - return 1; -END_VI_FUNC - -VI_FUNC(setAddress) - std::string key=s.as(1); - uint32_t val=s.as(2); - vif->setAddress(key,val); - return 0; -END_VI_FUNC - -VI_FUNC(getAddress) - std::string key=s.as(1); - - s.push(vif->getAddress(key)); - return 1; -END_VI_FUNC - -VI_FUNC(setOS) - unsigned os=s.as(1); - vif->setOS((DFHack::OSType)os); - return 0; -END_VI_FUNC - -VI_FUNC(getOS) - s.push(vif->getOS()); - return 1; -END_VI_FUNC -#undef VI_FUNC -#undef END_VI_FUNC -#define VI_FUNC(name) {#name,lua_VI_ ## name} -const luaL_Reg lua_vinfo_func[]= -{ - VI_FUNC(getBase), - VI_FUNC(setBase), - VI_FUNC(rebaseTo), - VI_FUNC(addMD5), - VI_FUNC(hasMD5), - VI_FUNC(addPE), - VI_FUNC(hasPE), - VI_FUNC(setVersion), - VI_FUNC(getVersion), - VI_FUNC(setAddress), - VI_FUNC(getAddress), - VI_FUNC(setOS), - VI_FUNC(getOS), - {NULL,NULL} -}; -#undef VI_FUNC -void lua::RegisterVersionInfo(lua::state &st) -{ - - st.getglobal("VersionInfo"); - if(st.is()) - { - st.pop(); - st.newtable(); - } - - lua::RegFunctionsLocal(st, lua_vinfo_func); - st.setglobal("VersionInfo"); -} From 923ea3f4b0a2c435399dd967d4736e2ec626344f Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 7 Oct 2012 20:44:18 +0300 Subject: [PATCH 23/79] Reactionhooks more usefull and gm-editor minor tweaks (e.g. search in containers) --- library/include/LuaTools.h | 15 +++++++++++++++ plugins/reactionhooks.cpp | 9 ++++++--- scripts/gui/gm-editor.lua | 21 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 3330e23e7..e956a65d5 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -472,3 +472,18 @@ namespace DFHack {namespace Lua { name##_event.invoke(out, 5); \ } \ } + +#define DEFINE_LUA_EVENT_6(name, handler, arg_type1, arg_type2, arg_type3, arg_type4, arg_type5,arg_type6) \ + 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, arg_type6 arg6) { \ + handler(out, arg1, arg2, arg3, arg4, arg5, arg6); \ + if (auto state = name##_event.state_if_count()) { \ + 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); \ + DFHack::Lua::Push(state, arg6); \ + name##_event.invoke(out, 6); \ + } \ +} \ No newline at end of file diff --git a/plugins/reactionhooks.cpp b/plugins/reactionhooks.cpp index 4041b99a5..d70fb9ea1 100644 --- a/plugins/reactionhooks.cpp +++ b/plugins/reactionhooks.cpp @@ -184,9 +184,10 @@ df::item* find_item( return NULL; } -static void handle_reaction_done(color_ostream &out, df::unit *unit, std::vector *in_items, std::vector *out_items,bool *call_native){}; +static void handle_reaction_done(color_ostream &out,df::reaction*, df::unit *unit, std::vector *in_items,std::vector *in_reag + , std::vector *out_items,bool *call_native){}; -DEFINE_LUA_EVENT_4(onReactionComplete, handle_reaction_done, df::unit *, std::vector *,std::vector *,bool *); +DEFINE_LUA_EVENT_6(onReactionComplete, handle_reaction_done,df::reaction*, df::unit *, std::vector *,std::vector *,std::vector *,bool *); DFHACK_PLUGIN_LUA_EVENTS { @@ -207,9 +208,11 @@ struct product_hook : item_product { ) { if (auto product = products[this]) { + df::reaction* this_reaction=product->react; + CoreSuspendClaimer suspend; color_ostream_proxy out(Core::getInstance().getConsole()); bool call_native=true; - onReactionComplete(out,unit,in_items,out_items,&call_native); + onReactionComplete(out,this_reaction,unit,in_items,in_reag,out_items,&call_native); if(!call_native) return; } diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua index 309663bdf..6999f1d8c 100644 --- a/scripts/gui/gm-editor.lua +++ b/scripts/gui/gm-editor.lua @@ -45,6 +45,25 @@ function GmEditorUi:init(args) return self end +function GmEditorUi:find(test) + local trg=self:currentTarget() + if trg.target and trg.target._kind and trg.target._kind=="container" then + if test== nil then + dialog.showInputPrompt("Test function","Input function that tests(k,v as argument):",COLOR_WHITE,"",dfhack.curry(self.find,self)) + return + end + local e,what=load("return function(k,v) return "..test.." end") + if e==nil then + dialog.showMessage("Error!","function failed to compile\n"..what,COLOR_RED) + end + for k,v in pairs(trg.target) do + if e()(k,v)==true then + self:pushTarget(v) + return + end + end + end +end function GmEditorUi:insertNew(typename) local tp=typename if typename== nil then @@ -165,6 +184,8 @@ end self:changeSelected(10) elseif keys.SELECT then self:editSelected() + elseif keys.CUSTOM_ALT_F then + self:find() elseif keys.CUSTOM_ALT_E then --self:specialEditor() elseif keys.CUSTOM_ALT_I then --insert From 49476818c466118508d2fe37bd8820a766e5a6c5 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 7 Oct 2012 20:45:14 +0300 Subject: [PATCH 24/79] Dfusion rebuild start (lua script side plugins) --- library/xml | 2 +- plugins/Dfusion/CMakeLists.txt | 2 +- plugins/Dfusion/dfusion.cpp | 148 ++++++--------------- plugins/Dfusion/luafiles/common.lua | 6 +- plugins/Dfusion/luafiles/embark/a.out | 0 plugins/Dfusion/luafiles/embark/embark.asm | 6 +- plugins/Dfusion/luafiles/embark/embark.o | Bin 348 -> 369 bytes plugins/Dfusion/src/OutFile.cpp | 18 +-- 8 files changed, 57 insertions(+), 125 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/embark/a.out diff --git a/library/xml b/library/xml index a914f3b75..8a78bfa21 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a914f3b7558335d53c0ac93f6e7267906a33cd29 +Subproject commit 8a78bfa218817765b0a80431e0cf25435ffb2179 diff --git a/plugins/Dfusion/CMakeLists.txt b/plugins/Dfusion/CMakeLists.txt index 65587201b..51f2e3bee 100644 --- a/plugins/Dfusion/CMakeLists.txt +++ b/plugins/Dfusion/CMakeLists.txt @@ -1,5 +1,5 @@ include_directories(include) -include_directories("${dfhack_SOURCE_DIR}/library/depends/tthread") + FILE(GLOB DFUSION_CPPS src/*.c*) set( DFUSION_CPPS_ALL diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index 0f49e860d..daacc0b26 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -6,16 +6,11 @@ #include #include - -#include "tinythread.h" - - #include "luamain.h" #include "lua_Process.h" #include "lua_Hexsearch.h" #include "lua_Misc.h" - #include "DataDefs.h" #include "LuaTools.h" @@ -23,112 +18,51 @@ using std::vector; using std::string; using namespace DFHack; -static tthread::mutex* mymutex=0; -static tthread::thread* thread_dfusion=0; -uint64_t timeLast=0; -DFHACK_PLUGIN("dfusion") -command_result dfusion (color_ostream &out, std::vector ¶meters); -command_result dfuse (color_ostream &out, std::vector ¶meters); +DFHACK_PLUGIN("dfusion") -DFhackCExport const char * plugin_name ( void ) +static int loadObjectFile(lua_State* L) { - return "dfusion"; + std::string path; + + path=luaL_checkstring(L,1); + + OutFile::File f(path); + lua_newtable(L); + int table_pos=lua_gettop(L); + size_t size=f.GetTextSize(); + Lua::Push(L,size); + lua_setfield(L,table_pos,"data_size"); + char* buf=new char[size]; + f.GetText(buf); + + //Lua::PushDFObject(L,DFHack::,buf); + //Lua::Push(L,buf); + lua_pushlightuserdata(L,buf); + lua_setfield(L,table_pos,"data"); + OutFile::vSymbol& symbols=f.GetSymbols(); + lua_newtable(L); + for(size_t i=0;i &commands) +DFHACK_PLUGIN_LUA_COMMANDS { + DFHACK_LUA_COMMAND(loadObjectFile), + DFHACK_LUA_END +}; +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - lua::state st=lua::glua::Get(); - - //maybe remake it to run automatically - Lua::Open(out, st); - - lua::RegisterProcess(st); - lua::RegisterHexsearch(st); - lua::RegisterMisc(st); - - #ifdef LINUX_BUILD - st.push(1); - st.setglobal("LINUX"); - #else - st.push(1); - st.setglobal("WINDOWS"); - #endif - - commands.push_back(PluginCommand("dfusion","Run dfusion system (interactive i.e. can input further commands).",dfusion,true)); - commands.push_back(PluginCommand("dfuse","Init dfusion system (not interactive).",dfuse,false)); - mymutex=new tthread::mutex; return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( Core * c ) -{ - -// shutdown stuff - if(thread_dfusion) - delete thread_dfusion; - delete mymutex; - return CR_OK; -} - -DFhackCExport command_result plugin_onupdate_DISABLED ( Core * c ) -{ - uint64_t time2 = GetTimeMs64(); - uint64_t delta = time2-timeLast; - if(delta<100) - return CR_OK; - timeLast = time2; - mymutex->lock(); - lua::state s=lua::glua::Get(); - s.getglobal("OnTick"); - if(s.is()) - { - try{ - s.pcall(); - } - catch(lua::exception &e) - { - c->getConsole().printerr("Error OnTick:%s\n",e.what()); - c->getConsole().printerr("%s\n",lua::DebugDump(lua::glua::Get()).c_str()); - c->getConsole().msleep(1000); - } - } - s.settop(0); - mymutex->unlock(); - return CR_OK; -} -void RunDfusion(color_ostream &out, std::vector ¶meters) -{ - mymutex->lock(); - lua::state s=lua::glua::Get(); - try{ - s.loadfile("dfusion/init.lua"); //load script - for(size_t i=0;iunlock(); -} -command_result dfuse(color_ostream &out, std::vector ¶meters) -{ - lua::state s=lua::glua::Get(); - s.push(1); - s.setglobal("INIT"); - RunDfusion(out,parameters); - return CR_OK; -} -command_result dfusion (color_ostream &out, std::vector ¶meters) -{ - lua::state s=lua::glua::Get(); - s.push(); - s.setglobal("INIT"); - RunDfusion(out,parameters); - return CR_OK; -} +} \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index 7e621c4ea..a6781b385 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -541,8 +541,4 @@ function Allocate(size) curptr=curptr+size engine.poked(ptr,curptr) return curptr-size+ptr -end -dofile("dfusion/patterns.lua") -dofile("dfusion/patterns2.lua") -dofile("dfusion/itempatterns.lua") -dofile("dfusion/buildingpatterns.lua") \ No newline at end of file +end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/embark/a.out b/plugins/Dfusion/luafiles/embark/a.out deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/Dfusion/luafiles/embark/embark.asm b/plugins/Dfusion/luafiles/embark/embark.asm index 644459ce5..d2fa91081 100644 --- a/plugins/Dfusion/luafiles/embark/embark.asm +++ b/plugins/Dfusion/luafiles/embark/embark.asm @@ -1,7 +1,7 @@ .intel_syntax -mov eax , [esp+0x1C] -caste: +mov eax , [esp+0x1C] # loop counter +mark_caste: movsx ecx, word ptr[eax*2+0xdeadbeef] -race: +mark_race: movzx eax,word ptr [eax*2+0xDEADBEEF] ret diff --git a/plugins/Dfusion/luafiles/embark/embark.o b/plugins/Dfusion/luafiles/embark/embark.o index 1c9c837c840eead962d5364f774aa47869b08db2..87f5bbd68f5c8d0aaf6f81485f653a440145a952 100644 GIT binary patch delta 59 ycmcb^^pR=832_D>V1Z&rAk796VBnv4zfw{X$jD7B%8pM?EG|ifFpCnCQyBnH+YLDY delta 38 ocmey!bcboe3Gw8_;*wMb1_l-&W&~n3hN8seRECN7D_KB70MFV8=l}o! diff --git a/plugins/Dfusion/src/OutFile.cpp b/plugins/Dfusion/src/OutFile.cpp index 2d8399b03..5617175f8 100644 --- a/plugins/Dfusion/src/OutFile.cpp +++ b/plugins/Dfusion/src/OutFile.cpp @@ -1,19 +1,21 @@ #include "OutFile.h" +#include using namespace OutFile; File::File(std::string path) { //mystream.exceptions ( std::fstream::eofbit | std::fstream::failbit | std::fstream::badbit ); mystream.open(path.c_str(),std::fstream::binary|std::ios::in|std::ios::out); - mystream.read((char*)&myhead,sizeof(myhead)); - for(unsigned i=0;i Date: Wed, 17 Oct 2012 19:33:20 +0300 Subject: [PATCH 25/79] Just companion orders tool --- scripts/gui/companion-order.lua | 398 ++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 scripts/gui/companion-order.lua diff --git a/scripts/gui/companion-order.lua b/scripts/gui/companion-order.lua new file mode 100644 index 000000000..6b6a79aa2 --- /dev/null +++ b/scripts/gui/companion-order.lua @@ -0,0 +1,398 @@ + +local gui = require 'gui' +local dlg = require 'gui.dialogs' + +local cursor=xyz2pos(df.global.cursor.x,df.global.cursor.y,df.global.cursor.z) +local permited_equips={} + +permited_equips[df.item_backpackst]="UPPERBODY" +permited_equips[df.item_flaskst]="UPPERBODY" +permited_equips[df.item_armorst]="UPPERBODY" +permited_equips[df.item_shoesst]="STANCE" +permited_equips[df.item_glovesst]="GRASP" +permited_equips[df.item_helmst]="HEAD" +permited_equips[df.item_pantsst]="LOWERBODY" +function DoesHaveSubtype(item) + if df.item_backpackst:is_instance(item) or df.item_flaskst:is_instance(item) then + return false + end + return true +end +function CheckCursor(p) + if p.x==-30000 then + dlg.showMessage( + 'Companion orders', + 'You must have a cursor on some tile!', COLOR_LIGHTRED + ) + return false + end + return true +end +function GetCaste(race_id,caste_id) + local race=df.creature_raw.find(race_id) + return race.caste[caste_id] +end + +function EnumBodyEquipable(race_id,caste_id) + local caste=GetCaste(race_id,caste_id) + local bps=caste.body_info.body_parts + local ret={} + for k,v in pairs(bps) do + ret[k]={bp=v,layers={[0]={size=0,permit=0},[1]={size=0,permit=0},[2]={size=0,permit=0},[3]={size=0,permit=0} } } + end + return ret +end +function ReadCurrentEquiped(body_equip,unit) + for k,v in pairs(unit.inventory) do + if v.mode==2 then + local bpid=v.body_part_id + if DoesHaveSubtype(v.item) then + local sb=v.item.subtype.props + local trg=body_equip[bpid] + local trg_layer=trg.layers[sb.layer] + + if trg_layer.permit==0 then + trg_layer.permit=sb.layer_permit + else + if trg_layer.permit>sb.layer_permit then + trg_layer.permit=sb.layer_permit + end + end + trg_layer.size=trg_layer.size+sb.layer_size + end + end + end +end +function LayeringPermits(body_part,item) + if not DoesHaveSubtype(item) then + return true + end + local sb=item.subtype.props + local trg_layer=body_part.layers[sb.layer] + if math.min(trg_layer.permit ,sb.layer_permit)0 and #items>0 do + if(dfhack.items.moveToInventory(items[#items],v,1,grasps[#grasps])) then + table.remove(grasps) + end + table.remove(items) + end + local backpack=GetBackpack(v) + if backpack then + while #items>0 do + dfhack.items.moveToContainer(items[#items],backpack) + table.remove(items) + end + end + end + return true +end}, +{name="unequip",f=function (unit_list) + --remove and drop all the stuff (todo maybe a gui too?) + for k,v in pairs(unit_list) do + while #v.inventory ~=0 do + dfhack.items.moveToGround(v.inventory[0].item,v.pos) + end + end + return true +end}, +--[=[ +{name="roam not working :<",f=function (unit_list,pos,dist) --does not work + if not CheckCursor(pos) then + return false + end + dist=dist or 5 + for k,v in pairs(unit_list) do + v.idle_area:assign(pos) + v.idle_area_threshold=dist + end + return true +end}, +--]=] +{name="wait",f=function (unit_list) + for k,v in pairs(unit_list) do + v.relations.group_leader_id=-1 + end + return true +end}, +{name="follow",f=function (unit_list) + local adv=df.global.world.units.active[0] + for k,v in pairs(unit_list) do + v.relations.group_leader_id=adv.id + end + return true +end}, +{name="leave",f=function (unit_list) + local adv=df.global.world.units.active[0] + local t_nem=dfhack.units.getNemesis(adv) + for k,v in pairs(unit_list) do + + v.relations.group_leader_id=-1 + local u_nem=dfhack.units.getNemesis(v) + if u_nem then + u_nem.group_leader_id=-1 + end + if t_nem and u_nem then + for k,v in pairs(t_nem.companions) do + if v==u_nem.id then + t_nem.companions:erase(k) + break + end + end + end + end + return true +end}, +} +local cheats={} +--[[ todo: add cheats...]]-- +function getCompanions(unit) + unit=unit or df.global.world.units.active[0] + local t_nem=dfhack.units.getNemesis(unit) + if t_nem==nil then + qerror("Invalid unit! No nemesis record") + end + local ret={} + for k,v in pairs(t_nem.companions) do + local u=df.nemesis_record.find(v) + if u.unit then + table.insert(ret,u.unit) + end + end + return ret +end + + +CompanionUi=defclass(CompanionUi,gui.FramedScreen) +CompanionUi.ATTRS{ + frame_title = "Companions", +} +function CompanionUi:init(args) + self.unit_list=args.unit_list + self.selected={} + for i=0,26 do + self.selected[i]=true + end +end +function CompanionUi:GetSelectedUnits() + local ret={} + for k,v in pairs(self.unit_list) do + if self.selected[k] then + table.insert(ret,v) + end + end + return ret +end +function CompanionUi:onInput(keys) + + + if keys.LEAVESCREEN then + self:dismiss() + elseif keys._STRING then + local s=keys._STRING + if s==string.byte('*') then + local v=self.selected[1] or false + for i=0,26 do + + self.selected[i]=not v + end + end + if s>=string.byte('a') and s<=string.byte('z') then + local idx=s-string.byte('a')+1 + if self.selected[idx] then + self.selected[idx]=false + else + self.selected[idx]=true + end + end + if s>=string.byte('A') and s<=string.byte('Z') then + local idx=s-string.byte('A')+1 + if orders[idx] and orders[idx].f then + if orders[idx].f(self:GetSelectedUnits(),cursor) then + self:dismiss() + end + end + --do order + end + end +end +function CompanionUi:onRenderBody( dc) + --list widget goes here... + local char_a=string.byte('a')-1 + dc:newline(1):string("*. All") + for k,v in ipairs(self.unit_list) do + if self.selected[k] then + dc:pen(COLOR_GREEN) + else + dc:pen(COLOR_GREY) + end + dc:newline(1):string(string.char(k+char_a)..". "):string(dfhack.TranslateName(v.name)); + end + dc:pen(COLOR_GREY) + local w,h=self:getWindowSize() + local char_A=string.byte('A')-1 + for k,v in ipairs(orders) do + dc:seek(w/2,k):string(string.char(k+char_A)..". "):string(v.name); + end +end +local screen=CompanionUi{unit_list=getCompanions()} +screen:show() \ No newline at end of file From da92fb9a1c8dfd373ac9c0781729fe722009fdcb Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 17 Oct 2012 21:43:44 +0300 Subject: [PATCH 26/79] Start of dfusion module. Fixed small error in memscan.lua and start of custom embark command. --- library/lua/memscan.lua | 2 +- plugins/Dfusion/luafiles/embark/init.lua | 25 ++++ plugins/lua/dfusion.lua | 163 +++++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 plugins/lua/dfusion.lua diff --git a/library/lua/memscan.lua b/library/lua/memscan.lua index ba3efd708..6796b3563 100644 --- a/library/lua/memscan.lua +++ b/library/lua/memscan.lua @@ -24,7 +24,7 @@ function CheckedArray:__len() return self.count end function CheckedArray:__index(idx) - if type(idx) == number then + if type(idx) == "number" then if idx >= self.count then error('Index out of bounds: '..tostring(idx)) end diff --git a/plugins/Dfusion/luafiles/embark/init.lua b/plugins/Dfusion/luafiles/embark/init.lua index 529c2d1e5..aa8f2822d 100644 --- a/plugins/Dfusion/luafiles/embark/init.lua +++ b/plugins/Dfusion/luafiles/embark/init.lua @@ -1,3 +1,28 @@ +local dfu=require("dfusion") +local ms=require("memscan") + +CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin) +CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} +function CustomEmbark:install() + local stoff=dfhack.internal.getAddress('start_dwarf_count') + if stoff==nil then + error("address for start_dwarf_count not found!") + end + local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) + local needle={0x0f,0xb7,0x0d} --movzx,... + add_dword(needle,race_id_offset) -- ...word ptr[] + local mem=ms.get_code_segment() + local trg_offset=mem.uint8_t.find(needle,stoff)--maybe endoff=stoff+bignumber + if trg_offset==nil then + error("address for race_load not found") + end + needle={0x83,0xc8,0xff} -- or eax, 0xFF + local caste_offset=mem.uint8_t.find(needle,trg_offset) + if caste_offset==nil or caste_offset-stoff>1000 then + error("Caste change code not found or found too far!") + end + +end function MakeTable(modpos,modsize,names) count=0 castes={} diff --git a/plugins/lua/dfusion.lua b/plugins/lua/dfusion.lua new file mode 100644 index 000000000..06128d29b --- /dev/null +++ b/plugins/lua/dfusion.lua @@ -0,0 +1,163 @@ +-- Stuff used by dfusion +local _ENV = mkmodule('plugins.dfusion') + +local ms=require("memscan") + +local marker={0xDE,0xAD,0xBE,0xEF} +patches={} +-- A reversable binary patch +BinaryPatch=defclass(BinaryPatch) +BinaryPatch.ATTRS {pre_data=DEFAULT_NIL,data=DEFAULT_NIL,address=DEFAULT_NIL,name=DEFAULT_NIL} +function BinaryPatch:init(args) + self.is_applied=false + if args.pre_data==nil or args.data==nil or args.address==nil or args.name==nil then + error("Invalid parameters to binary patch") + end + if patches[self.name]~=nil then + error("Patch already exist") + end + self.max_val=0 + for k,v in pairs(args.pre_data) do + if type(k)~="number" then + error("non number key in pre_data") + end + if self.max_val Date: Sat, 20 Oct 2012 01:38:08 +1300 Subject: [PATCH 27/79] Fix autobutcher resume --- plugins/zone.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index c496f49b6..8e73ba34b 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -3411,7 +3411,6 @@ command_result start_autobutcher(color_ostream &out) if (!config_autobutcher.isValid()) { config_autobutcher = pworld->AddPersistentData("autobutcher/config"); - config_autobutcher.ival(0) = enable_autobutcher; config_autobutcher.ival(1) = sleep_autobutcher; config_autobutcher.ival(2) = enable_autobutcher_autowatch; config_autobutcher.ival(3) = default_fk; @@ -3420,6 +3419,7 @@ command_result start_autobutcher(color_ostream &out) config_autobutcher.ival(6) = default_ma; } + config_autobutcher.ival(0) = enable_autobutcher; out << "Starting autobutcher." << endl; init_autobutcher(out); return CR_OK; From fd60db44ab415c0e59266380133c64ef3230b660 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sat, 20 Oct 2012 01:38:28 +1300 Subject: [PATCH 28/79] Search plugin, early work. Unit and stocks screen. --- plugins/CMakeLists.txt | 1 + plugins/search.cpp | 327 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 plugins/search.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 91d578215..fc3d0743e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -117,6 +117,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(forceequip forceequip.cpp) DFHACK_PLUGIN(manipulator manipulator.cpp) + DFHACK_PLUGIN(search search.cpp) # this one exports functions to lua DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) diff --git a/plugins/search.cpp b/plugins/search.cpp new file mode 100644 index 000000000..6a5a713c9 --- /dev/null +++ b/plugins/search.cpp @@ -0,0 +1,327 @@ +// Dwarf Manipulator - a Therapist-style labor editor + +#include +#include +#include +#include + +#include + +#include "df/viewscreen_unitlistst.h" +#include "df/viewscreen_storesst.h" +#include "df/interface_key.h" + +using std::set; +using std::vector; +using std::string; + +using namespace DFHack; +using namespace df::enums; + +using df::global::gps; + + +void OutputString(int8_t color, int &x, int y, const std::string &text) +{ + Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); + x += text.length(); +} + +struct search_struct +{ + static string search_string; + static bool entry_mode; + + void print_search_option(int &x) + { + OutputString(12, x, gps->dimy - 2, "s"); + OutputString(15, x, gps->dimy - 2, ": Search"); + if (search_string.length() > 0 || entry_mode) + OutputString(15, x, gps->dimy - 2, ": " + search_string); + if (entry_mode) + OutputString(12, x, gps->dimy - 2, "_"); + } + + bool process_input(df::interface_key select_key, set *input, bool &string_changed) + { + bool key_processed = true; + string_changed = false; + + if (entry_mode) + { + df::interface_key last_token = *input->rbegin(); + if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) + { + search_string += 32 + last_token - interface_key::STRING_A032; + string_changed = true; + } + else if (last_token == interface_key::STRING_A000) + { + if (search_string.length() > 0) + { + search_string.erase(search_string.length()-1); + string_changed = true; + } + } + else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) + { + entry_mode = false; + } + else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) + { + entry_mode = false; + key_processed = false; + } + } + else if (input->count(select_key)) + { + entry_mode = true; + } + else + { + key_processed = false; + } + + return key_processed; + } +}; + +string search_struct::search_string = ""; +bool search_struct::entry_mode = false; + + +struct stocks_search : df::viewscreen_storesst, search_struct +{ + typedef df::viewscreen_storesst interpose_base; + + static vector saved_items; + + static void reset_search() + { + entry_mode = false; + search_string = ""; + saved_items.clear(); + } + + void clear_search() + { + if (saved_items.size() > 0) + { + items = saved_items; + } + } + + + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + int x = 1; + print_search_option(x); + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + if (in_group_mode) + { + INTERPOSE_NEXT(feed)(input); + return; + } + + + bool string_changed = false; + + if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !in_right_list) + { + saved_items.clear(); + entry_mode = false; + if (search_string.length() > 0) + string_changed = true; + INTERPOSE_NEXT(feed)(input); + } + else + { + if (!process_input(interface_key::CUSTOM_S, input, string_changed) && !entry_mode) + { + INTERPOSE_NEXT(feed)(input); + if (in_group_mode) + { + clear_search(); + reset_search(); + } + } + } + + if (string_changed) + { + if (search_string.length() == 0) + { + clear_search(); + return; + } + + if (saved_items.size() == 0 && items.size() > 0) + { + saved_items = items; + } + items.clear(); + + for (int i = 0; i < saved_items.size(); i++ ) + { + string search_string_l = toLower(search_string); + string desc = Items::getDescription(saved_items[i], 0, true); + if (desc.find(search_string_l) != string::npos) + { + items.push_back(saved_items[i]); + } + } + + item_cursor = 0; + } + } + +}; + +vector stocks_search::saved_items; + + +IMPLEMENT_VMETHOD_INTERPOSE(stocks_search, feed); +IMPLEMENT_VMETHOD_INTERPOSE(stocks_search, render); + + + + +struct unitlist_search : df::viewscreen_unitlistst, search_struct +{ + typedef df::viewscreen_unitlistst interpose_base; + + static vector saved_units; + static vector saved_jobs; + + + static void reset_search() + { + entry_mode = false; + search_string = ""; + saved_units.clear(); + saved_jobs.clear(); + } + + void clear_search() + { + if (saved_units.size() > 0) + { + units[page] = saved_units; + jobs[page] = saved_jobs; + } + } + + void do_search() + { + if (search_string.length() == 0) + { + clear_search(); + return; + } + + while(1) + { + if (saved_units.size() == 0) + { + saved_units = units[page]; + saved_jobs = jobs[page]; + } + units[page].clear(); + jobs[page].clear(); + + for (int i = 0; i < saved_units.size(); i++ ) + { + df::unit *unit = saved_units[i]; + string search_string_l = toLower(search_string); + string name = toLower(Translation::TranslateName(Units::getVisibleName(unit), false)); + if (name.find(search_string_l) != string::npos) + { + units[page].push_back(unit); + jobs[page].push_back(saved_jobs[i]); + } + } + + if (units[page].size() > 0) + { + cursor_pos[page] = 0; + break; + } + + search_string.erase(search_string.length()-1); + } + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + bool string_changed = false; + if (!process_input(interface_key::CUSTOM_S, input, string_changed)) + { + if (!entry_mode) + { + if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) + { + clear_search(); + reset_search(); + } + INTERPOSE_NEXT(feed)(input); + } + } + else if (string_changed) + do_search(); + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + if (units[page].size()) + { + int x = 28; + print_search_option(x); + } + } + +}; + +vector unitlist_search::saved_units; +vector unitlist_search::saved_jobs; + + +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search, feed); +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search, render); + + + + +DFHACK_PLUGIN("search"); + + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + if (!gps || !INTERPOSE_HOOK(unitlist_search, feed).apply() || !INTERPOSE_HOOK(unitlist_search, render).apply() + || !INTERPOSE_HOOK(stocks_search, feed).apply() || !INTERPOSE_HOOK(stocks_search, render).apply()) + out.printerr("Could not insert Search hooks!\n"); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + INTERPOSE_HOOK(unitlist_search, feed).remove(); + INTERPOSE_HOOK(unitlist_search, render).remove(); + INTERPOSE_HOOK(stocks_search, feed).remove(); + INTERPOSE_HOOK(stocks_search, render).remove(); + return CR_OK; +} + +DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event ) +{ + unitlist_search::reset_search(); + return CR_OK; +} \ No newline at end of file From c5b38a24eb86e9ec28eb290ad2aa3a0a2b5781e4 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 21 Oct 2012 14:18:29 +1300 Subject: [PATCH 29/79] Refactoring to use templates --- plugins/search.cpp | 576 +++++++++++++++++++++++++-------------------- 1 file changed, 316 insertions(+), 260 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index 6a5a713c9..e911a73be 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -20,283 +20,338 @@ using namespace df::enums; using df::global::gps; - void OutputString(int8_t color, int &x, int y, const std::string &text) { Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); x += text.length(); } -struct search_struct + +// +// START: Base Search functionality +// +template +struct search_parent { - static string search_string; - static bool entry_mode; - - void print_search_option(int &x) - { - OutputString(12, x, gps->dimy - 2, "s"); - OutputString(15, x, gps->dimy - 2, ": Search"); - if (search_string.length() > 0 || entry_mode) - OutputString(15, x, gps->dimy - 2, ": " + search_string); - if (entry_mode) - OutputString(12, x, gps->dimy - 2, "_"); - } - - bool process_input(df::interface_key select_key, set *input, bool &string_changed) - { - bool key_processed = true; - string_changed = false; - - if (entry_mode) - { - df::interface_key last_token = *input->rbegin(); - if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) - { - search_string += 32 + last_token - interface_key::STRING_A032; - string_changed = true; - } - else if (last_token == interface_key::STRING_A000) - { - if (search_string.length() > 0) - { - search_string.erase(search_string.length()-1); - string_changed = true; - } - } - else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) - { - entry_mode = false; - } - else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) - { - entry_mode = false; - key_processed = false; - } - } - else if (input->count(select_key)) - { - entry_mode = true; - } - else - { - key_processed = false; - } - - return key_processed; - } + vector *sort_list1; + vector *sort_list2; + int *cursor_pos; + char select_key; + const S *viewscreen; + + bool valid; + bool entry_mode; + bool redo_search; + string search_string; + vector saved_list1; + vector saved_list2; + + df::interface_key select_token; + const int ascii_to_enum_offset; + + search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0') + { + reset_all(); + } + + virtual void init(int *cursor_pos, vector *sort_list1, vector *sort_list2 = NULL, char select_key = 's') + { + this->cursor_pos = cursor_pos; + this->sort_list1 = sort_list1; + this->sort_list2 = sort_list2; + this->select_key = select_key; + select_token = (df::interface_key) (ascii_to_enum_offset + select_key); + valid = true; + } + + void reset_search() + { + entry_mode = false; + search_string = ""; + saved_list1.clear(); + saved_list2.clear(); + } + + void reset_all() + { + reset_search(); + valid = false; + sort_list1 = NULL; + sort_list2 = NULL; + viewscreen = NULL; + select_key = 's'; + } + + void clear_search() + { + if (saved_list1.size() > 0) + { + *sort_list1 = saved_list1; + if (sort_list2 != NULL) + *sort_list2 = saved_list2; + } + } + + void do_search() + { + if (search_string.length() == 0) + { + clear_search(); + return; + } + + if (saved_list1.size() == 0) + { + saved_list1 = *sort_list1; + if (sort_list2 != NULL) + saved_list2 = *sort_list2; + } + sort_list1->clear(); + if (sort_list2 != NULL) + sort_list2->clear(); + + string search_string_l = toLower(search_string); + for (int i = 0; i < saved_list1.size(); i++ ) + { + T *element = saved_list1[i]; + string desc = toLower(get_element_description(element)); + if (desc.find(search_string_l) != string::npos) + { + sort_list1->push_back(element); + if (sort_list2 != NULL) + sort_list2->push_back(saved_list2[i]); + } + } + + *cursor_pos = 0; + } + + virtual bool process_input(const set *input) + { + bool key_processed = true; + + if (entry_mode) + { + df::interface_key last_token = *input->rbegin(); + if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) + { + search_string += last_token - ascii_to_enum_offset; + do_search(); + } + else if (last_token == interface_key::STRING_A000) + { + if (search_string.length() > 0) + { + search_string.erase(search_string.length()-1); + do_search(); + } + } + else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) + { + entry_mode = false; + } + else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN) + || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) + { + entry_mode = false; + key_processed = false; + } + } + else if (input->count(select_token)) + { + entry_mode = true; + } + else + { + key_processed = false; + } + + return key_processed; + } + + + virtual string get_element_description(T *element) const = 0; + virtual void render () const = 0; + + virtual void do_post_update_check() + { + if (redo_search) + { + do_search(); + redo_search = false; + } + } + + void print_search_option(int x) const + { + OutputString((entry_mode) ? 4 : 12, x, gps->dimy - 2, string(1, select_key)); + OutputString((entry_mode) ? 10 : 15, x, gps->dimy - 2, ": Search"); + if (search_string.length() > 0 || entry_mode) + OutputString(15, x, gps->dimy - 2, ": " + search_string); + if (entry_mode) + OutputString(10, x, gps->dimy - 2, "_"); + } }; -string search_struct::search_string = ""; -bool search_struct::entry_mode = false; - -struct stocks_search : df::viewscreen_storesst, search_struct +template +struct search_hook : T { - typedef df::viewscreen_storesst interpose_base; - - static vector saved_items; - - static void reset_search() - { - entry_mode = false; - search_string = ""; - saved_items.clear(); - } - - void clear_search() - { - if (saved_items.size() > 0) - { - items = saved_items; - } - } - - - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - int x = 1; - print_search_option(x); - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - if (in_group_mode) - { - INTERPOSE_NEXT(feed)(input); - return; - } - - - bool string_changed = false; - - if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !in_right_list) - { - saved_items.clear(); - entry_mode = false; - if (search_string.length() > 0) - string_changed = true; - INTERPOSE_NEXT(feed)(input); - } - else - { - if (!process_input(interface_key::CUSTOM_S, input, string_changed) && !entry_mode) - { - INTERPOSE_NEXT(feed)(input); - if (in_group_mode) - { - clear_search(); - reset_search(); - } - } - } - - if (string_changed) - { - if (search_string.length() == 0) - { - clear_search(); - return; - } - - if (saved_items.size() == 0 && items.size() > 0) - { - saved_items = items; - } - items.clear(); - - for (int i = 0; i < saved_items.size(); i++ ) - { - string search_string_l = toLower(search_string); - string desc = Items::getDescription(saved_items[i], 0, true); - if (desc.find(search_string_l) != string::npos) - { - items.push_back(saved_items[i]); - } - } - - item_cursor = 0; - } - } - + typedef T interpose_base; + + static V module; + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + module.init(this); + if (!module.process_input(input)) + { + INTERPOSE_NEXT(feed)(input); + module.do_post_update_check(); + } + + } + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + module.init(this); + INTERPOSE_NEXT(render)(); + module.render(); + } }; -vector stocks_search::saved_items; - +template V search_hook ::module; -IMPLEMENT_VMETHOD_INTERPOSE(stocks_search, feed); -IMPLEMENT_VMETHOD_INTERPOSE(stocks_search, render); +// +// END: Base Search functionality +// -struct unitlist_search : df::viewscreen_unitlistst, search_struct +// +// START: Stocks screen search +// +struct stocks_search : search_parent { - typedef df::viewscreen_unitlistst interpose_base; - - static vector saved_units; - static vector saved_jobs; - - - static void reset_search() - { - entry_mode = false; - search_string = ""; - saved_units.clear(); - saved_jobs.clear(); - } - - void clear_search() - { - if (saved_units.size() > 0) - { - units[page] = saved_units; - jobs[page] = saved_jobs; - } - } - - void do_search() - { - if (search_string.length() == 0) - { - clear_search(); - return; - } - - while(1) - { - if (saved_units.size() == 0) - { - saved_units = units[page]; - saved_jobs = jobs[page]; - } - units[page].clear(); - jobs[page].clear(); - - for (int i = 0; i < saved_units.size(); i++ ) - { - df::unit *unit = saved_units[i]; - string search_string_l = toLower(search_string); - string name = toLower(Translation::TranslateName(Units::getVisibleName(unit), false)); - if (name.find(search_string_l) != string::npos) - { - units[page].push_back(unit); - jobs[page].push_back(saved_jobs[i]); - } - } - - if (units[page].size() > 0) - { - cursor_pos[page] = 0; - break; - } - - search_string.erase(search_string.length()-1); - } - } - - DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) - { - bool string_changed = false; - if (!process_input(interface_key::CUSTOM_S, input, string_changed)) - { - if (!entry_mode) - { - if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) - { - clear_search(); - reset_search(); - } - INTERPOSE_NEXT(feed)(input); - } - } - else if (string_changed) - do_search(); - } - - DEFINE_VMETHOD_INTERPOSE(void, render, ()) - { - INTERPOSE_NEXT(render)(); - - if (units[page].size()) - { - int x = 28; - print_search_option(x); - } - } + virtual void render() const + { + if (!viewscreen->in_group_mode) + print_search_option(1); + else + { + int x = 1; + OutputString(15, x, gps->dimy - 2, "Tab to enable Search"); + } + } + + virtual string get_element_description(df::item *element) const + { + return Items::getDescription(element, 0, true); + } + + virtual bool process_input(const set *input) + { + if (viewscreen->in_group_mode) + return false; + + if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list) + { + saved_list1.clear(); + entry_mode = false; + if (search_string.length() > 0) + redo_search = true; + + return false; + } + else + return search_parent::process_input(input) || entry_mode; + + return true; + } + + virtual void do_post_update_check() + { + if (viewscreen->in_group_mode) + { + clear_search(); + reset_search(); + } + else + search_parent::do_post_update_check(); + } + + virtual void init(df::viewscreen_storesst *screen) + { + if (!valid) + { + viewscreen = screen; + search_parent::init(&screen->item_cursor, &screen->items); + } + } }; -vector unitlist_search::saved_units; -vector unitlist_search::saved_jobs; +typedef search_hook stocks_search_hook; +IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); + +// +// END: Stocks screen search +// -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search, feed); -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search, render); +// +// START: Unit screen search +// +struct unitlist_search : search_parent +{ + virtual void render() const + { + print_search_option(28); + } + + virtual string get_element_description(df::unit *element) const + { + return Translation::TranslateName(Units::getVisibleName(element), false); + } + + virtual bool process_input(const set *input) + { + if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) + { + if (!entry_mode) + { + clear_search(); + reset_search(); + return false; + } + } + else + return search_parent::process_input(input) || entry_mode; + + return true; + } + + virtual void init(df::viewscreen_unitlistst *screen) + { + if (!valid) + { + viewscreen = screen; + search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]); + } + } +}; + +typedef search_hook unitlist_search_hook; +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); + +// +// END: Unit screen search +// DFHACK_PLUGIN("search"); @@ -304,8 +359,8 @@ DFHACK_PLUGIN("search"); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { - if (!gps || !INTERPOSE_HOOK(unitlist_search, feed).apply() || !INTERPOSE_HOOK(unitlist_search, render).apply() - || !INTERPOSE_HOOK(stocks_search, feed).apply() || !INTERPOSE_HOOK(stocks_search, render).apply()) + if (!gps || !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_search_hook, render).apply() + || !INTERPOSE_HOOK(stocks_search_hook, feed).apply() || !INTERPOSE_HOOK(stocks_search_hook, render).apply()) out.printerr("Could not insert Search hooks!\n"); return CR_OK; @@ -313,15 +368,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector Date: Sun, 21 Oct 2012 13:42:55 +0300 Subject: [PATCH 30/79] More work on dfusion. Embark anywhere script separated. --- plugins/Dfusion/dfusion.cpp | 20 ++++ plugins/Dfusion/luafiles/embark/init.lua | 137 ++++++++--------------- plugins/devel/CMakeLists.txt | 1 - plugins/lua/dfusion.lua | 113 ++++++++++++------- scripts/dfusion.lua | 2 + scripts/embark.lua | 53 +++++++++ 6 files changed, 196 insertions(+), 130 deletions(-) create mode 100644 scripts/dfusion.lua create mode 100644 scripts/embark.lua diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index daacc0b26..4507f9a15 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -58,8 +58,28 @@ static int loadObjectFile(lua_State* L) lua_setfield(L,table_pos,"symbols"); return 1; } +static int markAsExecutable(lua_State* L) +{ + unsigned addr=luaL_checkunsigned(L,1); + std::vector ranges; + DFHack::Core::getInstance().p->getMemRanges(ranges); + for(size_t i=0;isetPermisions(ranges[i],newperm); + return 0; + } + } + lua_pushlstring(L,"Memory range not found",23); + lua_error(L); + return 0; +} DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_COMMAND(loadObjectFile), + DFHACK_LUA_COMMAND(markAsExecutable), DFHACK_LUA_END }; DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) diff --git a/plugins/Dfusion/luafiles/embark/init.lua b/plugins/Dfusion/luafiles/embark/init.lua index aa8f2822d..76be00c72 100644 --- a/plugins/Dfusion/luafiles/embark/init.lua +++ b/plugins/Dfusion/luafiles/embark/init.lua @@ -1,108 +1,69 @@ -local dfu=require("dfusion") +local dfu=require("plugins.dfusion") local ms=require("memscan") - +local MAX_RACES=100 CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin) CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} function CustomEmbark:install() local stoff=dfhack.internal.getAddress('start_dwarf_count') + if #self.race_caste_data<7 then + error("caste and race count must be bigger than 6") + end + if #self.race_caste_data>MAX_RACES then + error("caste and race count must be less then "..MAX_RACES) + end if stoff==nil then error("address for start_dwarf_count not found!") end local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) - local needle={0x0f,0xb7,0x0d} --movzx,... - add_dword(needle,race_id_offset) -- ...word ptr[] + print(string.format("start=%08x",stoff)) + local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id] + local tmp_table=dfu.dwordToTable(race_id_offset) + for k,v in ipairs(tmp_table) do + table.insert(needle,v) + end + local mem=ms.get_code_segment() - local trg_offset=mem.uint8_t.find(needle,stoff)--maybe endoff=stoff+bignumber + print(mem.uint8_t:addr2idx(stoff)) + print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff))) + local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber if trg_offset==nil then error("address for race_load not found") end + local call_data={0x90,0x90} + local _,data_offset=df.sizeof(self.data) + dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) + self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} needle={0x83,0xc8,0xff} -- or eax, 0xFF - local caste_offset=mem.uint8_t.find(needle,trg_offset) + local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) if caste_offset==nil or caste_offset-stoff>1000 then error("Caste change code not found or found too far!") end + self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} + self.disable_castes:apply() + self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} + self.dwarfcount:apply() + local caste_array=self:allocate("caste_array","uint16_t",#self.race_caste_data) + local race_array=self:allocate("race_array","uint16_t",#self.race_caste_data) + for k,v in ipairs(self.race_caste_data) do + caste_array[k-1]=v[2] + race_array[k-1]=v[1] + end + local race_array_off,caste_array_off + local _ + _,race_array_off=df.sizeof(race_array) + _,caste_array_off=df.sizeof(caste_array) + self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess... + self:set_marker_dword("caste",race_array_off) + + self:move_to_df() + self.call_patch:apply() + self.installed=true end -function MakeTable(modpos,modsize,names) - count=0 - castes={} - --print("Making table") - for _,line in pairs(names) do - --print("Line:"..line) - tpos=string.find(line,":") - if tpos~=nil then - --print("Was line:"..line) - table.insert(castes,tonumber(string.sub(line,tpos+1))) - line=string.sub(line,1,tpos-1) - --print("IS line:"..line) - else - table.insert(castes,-1) - end - if RaceTable[line] == nil then - error("Failure, "..line.." not found!") - end - --print("adding:"..line.." id:"..RaceTable[line]) - engine.pokew(modpos+modsize+count*2,RaceTable[line]) -- add race - count = count + 1 - end - i=0 - for _,caste in pairs(castes) do - - engine.pokew(modpos+modsize+count*2+i*2,caste) -- add caste - i= i+1 - end - - engine.poked(stoff,count) - return count -end -function embark(names) - RaceTable=RaceTable or BuildNameTable() - mypos=engine.getmod('Embark') - stoff=VersionInfo.getAddress('start_dwarf_count') - if mypos then --if mod already loaded - print("Mod already loaded @:"..mypos.." just updating") - modpos=mypos - _,modsize=engine.loadobj('dfusion/embark/embark.o') - - count=MakeTable(modpos,modsize,names) --just remake tables - else - - _,tofind=df.sizeof(df.global.ui:_field("race_id")) - - loc=offsets.find(stoff,0x0f,0xb7,0x0d,DWORD_,tofind) --MOVZX ECX,WORD PTR[] - - print(string.format("found:%x",loc)) - if((loc~=0)and(loc-stoff<1000)) then - loc2=offsets.find(loc,0x83,0xc8,0xff) -- or eax, ffffff (for caste) - if loc2== 0 then - error ("Location for caste nulling not found!") - end - engine.pokeb(loc2,0x90) - engine.pokeb(loc2+1,0x90) - engine.pokeb(loc2+2,0x90) - ModData=engine.installMod("dfusion/embark/embark.o","Embark",256) - modpos=ModData.pos - modsize=ModData.size - local castepos=modpos+engine.FindMarker(ModData,"caste") - local racepos=modpos+engine.FindMarker(ModData,"race") - count=MakeTable(modpos,modsize,names) - engine.poked(castepos,modpos+modsize) --fix array start for race - engine.poked(racepos,modpos+modsize+count*2) --fix array start for caste - print("sucess loading mod @:"..modpos) - -- build race vector after module. - - - --finaly poke in the call! - engine.pokeb(loc,0x90) - engine.pokeb(loc+1,0x90) - engine.pokeb(loc+2,0xe8) - engine.poked(loc+3,modpos-loc-7) - --engine.pokeb(loc+5,0x90) - - SetExecute(modpos) - else - error("did not find patch location, failing...") - end - - end +function CustomEmbark:uninstall() + if self.installed then + self.call_patch:remove() + self.disable_castes:remove() + self.dwarfcount:remove() + end end \ No newline at end of file diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index f126ae53b..134d5cb67 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -18,7 +18,6 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp) DFHACK_PLUGIN(rprobe rprobe.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) DFHACK_PLUGIN(vshook vshook.cpp) -DFHACK_PLUGIN(steam-engine steam-engine.cpp) IF(UNIX) DFHACK_PLUGIN(ref-index ref-index.cpp) ENDIF() diff --git a/plugins/lua/dfusion.lua b/plugins/lua/dfusion.lua index 06128d29b..46f28f78e 100644 --- a/plugins/lua/dfusion.lua +++ b/plugins/lua/dfusion.lua @@ -4,8 +4,23 @@ local _ENV = mkmodule('plugins.dfusion') local ms=require("memscan") local marker={0xDE,0xAD,0xBE,0xEF} -patches={} +--utility functions +function dwordToTable(dword) + local b={bit32.extract(dword,0,8),bit32.extract(dword,8,8),bit32.extract(dword,16,8),bit32.extract(dword,24,8)} + return b +end +function concatTables(t1,t2) + for k,v in pairs(t2) do + table.insert(t1,v) + end +end +function makeCall(from,to) + local ret={0xe8} + concatTables(ret,dwordToTable(to-from-5)) + return ret +end -- A reversable binary patch +patches={} BinaryPatch=defclass(BinaryPatch) BinaryPatch.ATTRS {pre_data=DEFAULT_NIL,data=DEFAULT_NIL,address=DEFAULT_NIL,name=DEFAULT_NIL} function BinaryPatch:init(args) @@ -16,19 +31,8 @@ function BinaryPatch:init(args) if patches[self.name]~=nil then error("Patch already exist") end - self.max_val=0 - for k,v in pairs(args.pre_data) do - if type(k)~="number" then - error("non number key in pre_data") - end - if self.max_val Date: Sun, 21 Oct 2012 13:46:12 +0300 Subject: [PATCH 31/79] Small error fix --- plugins/lua/dfusion.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/dfusion.lua b/plugins/lua/dfusion.lua index 46f28f78e..ac9a20868 100644 --- a/plugins/lua/dfusion.lua +++ b/plugins/lua/dfusion.lua @@ -52,7 +52,7 @@ function BinaryPatch:test() end function BinaryPatch:apply() if not self:test() then - error(string.format("pre-data for binary patch does not match expected") + error(string.format("pre-data for binary patch does not match expected")) end local post_buf=df.new('uint8_t',#self.pre_data) From bf01ecd206f4f3522d944162a1e806b7b45272a9 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Sun, 21 Oct 2012 21:32:01 +1300 Subject: [PATCH 32/79] Added Trade screen --- plugins/search.cpp | 365 ++++++++++++++++++++++++++++++--------------- 1 file changed, 245 insertions(+), 120 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index e911a73be..294ebe7d2 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -9,6 +9,7 @@ #include "df/viewscreen_unitlistst.h" #include "df/viewscreen_storesst.h" +#include "df/viewscreen_tradegoodsst.h" #include "df/interface_key.h" using std::set; @@ -31,25 +32,93 @@ void OutputString(int8_t color, int &x, int y, const std::string &text) // START: Base Search functionality // template -struct search_parent +class search_parent { - vector *sort_list1; - vector *sort_list2; - int *cursor_pos; - char select_key; +public: + void reset_all() + { + reset_search(); + valid = false; + sort_list1 = NULL; + sort_list2 = NULL; + viewscreen = NULL; + select_key = 's'; + } + + virtual bool process_input(set *input) + { + if (lock != NULL && lock != this) + return false; + + if (!should_check_input(input)) + return false; + + bool key_processed = true; + + if (entry_mode) + { + df::interface_key last_token = *input->rbegin(); + if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) + { + search_string += last_token - ascii_to_enum_offset; + do_search(); + } + else if (last_token == interface_key::STRING_A000) + { + if (search_string.length() > 0) + { + search_string.erase(search_string.length()-1); + do_search(); + } + } + else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) + { + end_entry_mode(); + } + else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN) + || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) + { + end_entry_mode(); + key_processed = false; + } + } + else if (input->count(select_token)) + { + start_entry_mode(); + } + else if (input->count((df::interface_key) (select_token + shift_offset))) + { + clear_search(); + } + else + { + key_processed = false; + } + + return key_processed || entry_mode; + } + + virtual void do_post_update_check() + { + if (redo_search) + { + do_search(); + redo_search = false; + } + } + + static search_parent *lock; + +protected: const S *viewscreen; + vector saved_list1; + vector saved_list2; bool valid; - bool entry_mode; bool redo_search; string search_string; - vector saved_list1; - vector saved_list2; - df::interface_key select_token; - const int ascii_to_enum_offset; - - search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0') + search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0'), shift_offset('A' - 'a') { reset_all(); } @@ -63,23 +132,30 @@ struct search_parent select_token = (df::interface_key) (ascii_to_enum_offset + select_key); valid = true; } + + bool is_entry_mode() + { + return entry_mode; + } + + void start_entry_mode() + { + entry_mode = true; + lock = this; + } - void reset_search() + void end_entry_mode() { entry_mode = false; - search_string = ""; - saved_list1.clear(); - saved_list2.clear(); + lock = NULL; } - void reset_all() + void reset_search() { - reset_search(); - valid = false; - sort_list1 = NULL; - sort_list2 = NULL; - viewscreen = NULL; - select_key = 's'; + end_entry_mode(); + search_string = ""; + saved_list1.clear(); + saved_list2.clear(); } void clear_search() @@ -90,6 +166,7 @@ struct search_parent if (sort_list2 != NULL) *sort_list2 = saved_list2; } + search_string = ""; } void do_search() @@ -126,75 +203,45 @@ struct search_parent *cursor_pos = 0; } - virtual bool process_input(const set *input) + virtual bool should_check_input(set *input) { - bool key_processed = true; - - if (entry_mode) - { - df::interface_key last_token = *input->rbegin(); - if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) - { - search_string += last_token - ascii_to_enum_offset; - do_search(); - } - else if (last_token == interface_key::STRING_A000) - { - if (search_string.length() > 0) - { - search_string.erase(search_string.length()-1); - do_search(); - } - } - else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) - { - entry_mode = false; - } - else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN) - || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) - { - entry_mode = false; - key_processed = false; - } - } - else if (input->count(select_token)) - { - entry_mode = true; - } - else - { - key_processed = false; - } - - return key_processed; + return true; } - - virtual string get_element_description(T *element) const = 0; - virtual void render () const = 0; - - virtual void do_post_update_check() + void print_search_option(int x, int y = -1) const { - if (redo_search) - { - do_search(); - redo_search = false; - } - } + if (y == -1) + y = gps->dimy - 2; - void print_search_option(int x) const - { - OutputString((entry_mode) ? 4 : 12, x, gps->dimy - 2, string(1, select_key)); - OutputString((entry_mode) ? 10 : 15, x, gps->dimy - 2, ": Search"); + OutputString((entry_mode) ? 4 : 12, x, y, string(1, select_key)); + OutputString((entry_mode) ? 10 : 15, x, y, ": Search"); if (search_string.length() > 0 || entry_mode) - OutputString(15, x, gps->dimy - 2, ": " + search_string); + OutputString(15, x, y, ": " + search_string); if (entry_mode) - OutputString(10, x, gps->dimy - 2, "_"); + OutputString(10, x, y, "_"); } + + virtual string get_element_description(T *element) const = 0; + virtual void render () const = 0; + +private: + vector *sort_list1; + vector *sort_list2; + int *cursor_pos; + char select_key; + + bool entry_mode; + + df::interface_key select_token; + const int ascii_to_enum_offset; + const int shift_offset; + }; +template search_parent *search_parent ::lock = NULL; -template + +template struct search_hook : T { typedef T interpose_base; @@ -220,7 +267,7 @@ struct search_hook : T } }; -template V search_hook ::module; +template V search_hook ::module; // @@ -232,8 +279,10 @@ template V search_hook ::module; // // START: Stocks screen search // -struct stocks_search : search_parent +class stocks_search : public search_parent { +public: + virtual void render() const { if (!viewscreen->in_group_mode) @@ -245,31 +294,6 @@ struct stocks_search : search_parent } } - virtual string get_element_description(df::item *element) const - { - return Items::getDescription(element, 0, true); - } - - virtual bool process_input(const set *input) - { - if (viewscreen->in_group_mode) - return false; - - if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list) - { - saved_list1.clear(); - entry_mode = false; - if (search_string.length() > 0) - redo_search = true; - - return false; - } - else - return search_parent::process_input(input) || entry_mode; - - return true; - } - virtual void do_post_update_check() { if (viewscreen->in_group_mode) @@ -290,6 +314,30 @@ struct stocks_search : search_parent } } + +private: + virtual string get_element_description(df::item *element) const + { + return Items::getDescription(element, 0, true); + } + + virtual bool should_check_input(set *input) + { + if (viewscreen->in_group_mode) + return false; + + if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list) + { + saved_list1.clear(); + end_entry_mode(); + if (search_string.length() > 0) + redo_search = true; + + return false; + } + + return true; + } }; @@ -306,51 +354,120 @@ IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); // // START: Unit screen search // -struct unitlist_search : search_parent +class unitlist_search : public search_parent { +public: + virtual void render() const { print_search_option(28); } + virtual void init(df::viewscreen_unitlistst *screen) + { + if (!valid) + { + viewscreen = screen; + search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]); + } + } + +private: virtual string get_element_description(df::unit *element) const { return Translation::TranslateName(Units::getVisibleName(element), false); } - virtual bool process_input(const set *input) + virtual bool should_check_input(set *input) { if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) { - if (!entry_mode) + if (!is_entry_mode()) { clear_search(); reset_search(); - return false; } + else + input->clear(); + + return false; } - else - return search_parent::process_input(input) || entry_mode; return true; } - virtual void init(df::viewscreen_unitlistst *screen) +}; + +typedef search_hook unitlist_search_hook; +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); + +// +// END: Unit screen search +// + + +// +// START: Trade screen search +// +class trade_search_base : public search_parent +{ + +private: + virtual string get_element_description(df::item *element) const + { + return Items::getDescription(element, 0, true); + } +}; + + +class trade_search_merc : public trade_search_base +{ +public: + virtual void render() const + { + print_search_option(2, 26); + } + + virtual void init(df::viewscreen_tradegoodsst *screen) { if (!valid) { viewscreen = screen; - search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]); + search_parent::init(&screen->trader_cursor, &screen->trader_items, NULL, 'q'); } } }; -typedef search_hook unitlist_search_hook; -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); +typedef search_hook trade_search_merc_hook; +IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, render); + + +class trade_search_fort : public trade_search_base +{ +public: + virtual void render() const + { + print_search_option(42, 26); + } + + virtual void init(df::viewscreen_tradegoodsst *screen) + { + if (!valid) + { + viewscreen = screen; + search_parent::init(&screen->broker_cursor, &screen->broker_items, NULL, 'w'); + } + } +}; + +typedef search_hook trade_search_fort_hook; +IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, feed); +IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, render); // -// END: Unit screen search +// END: Trade screen search // @@ -360,6 +477,8 @@ DFHACK_PLUGIN("search"); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { if (!gps || !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_search_hook, render).apply() + || !INTERPOSE_HOOK(trade_search_merc_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_merc_hook, render).apply() + || !INTERPOSE_HOOK(trade_search_fort_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_fort_hook, render).apply() || !INTERPOSE_HOOK(stocks_search_hook, feed).apply() || !INTERPOSE_HOOK(stocks_search_hook, render).apply()) out.printerr("Could not insert Search hooks!\n"); @@ -370,6 +489,10 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { INTERPOSE_HOOK(unitlist_search_hook, feed).remove(); INTERPOSE_HOOK(unitlist_search_hook, render).remove(); + INTERPOSE_HOOK(trade_search_merc_hook, feed).remove(); + INTERPOSE_HOOK(trade_search_merc_hook, render).remove(); + INTERPOSE_HOOK(trade_search_fort_hook, feed).remove(); + INTERPOSE_HOOK(trade_search_fort_hook, render).remove(); INTERPOSE_HOOK(stocks_search_hook, feed).remove(); INTERPOSE_HOOK(stocks_search_hook, render).remove(); return CR_OK; @@ -378,6 +501,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event ) { unitlist_search_hook::module.reset_all(); + trade_search_merc_hook::module.reset_all(); + trade_search_fort_hook::module.reset_all(); stocks_search_hook::module.reset_all(); return CR_OK; } From f501ae074870ffd629a9e086d2acdc0026bd5d66 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 22 Oct 2012 01:09:10 +1300 Subject: [PATCH 33/79] Bug fix on unit sort --- plugins/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index 294ebe7d2..9a9a28c40 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -385,7 +385,7 @@ private: if (!is_entry_mode()) { clear_search(); - reset_search(); + reset_all(); } else input->clear(); From c433a8eeff35911af1073beac87adbc7aab375c8 Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 22 Oct 2012 02:42:17 +1300 Subject: [PATCH 34/79] Better handling of Trade screen. Tracks marked items and handles re-orders is sort plugin is used to sort filtered list. --- plugins/search.cpp | 88 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 15 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index 9a9a28c40..81785d914 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -31,7 +31,7 @@ void OutputString(int8_t color, int &x, int y, const std::string &text) // // START: Base Search functionality // -template +template class search_parent { public: @@ -43,6 +43,7 @@ public: sort_list2 = NULL; viewscreen = NULL; select_key = 's'; + track_secondary_values = false; } virtual bool process_input(set *input) @@ -111,11 +112,13 @@ public: protected: const S *viewscreen; - vector saved_list1; - vector saved_list2; + vector saved_list1, reference_list; + vector saved_list2; + vector saved_indexes; bool valid; bool redo_search; + bool track_secondary_values; string search_string; search_parent() : ascii_to_enum_offset(interface_key::STRING_A048 - '0'), shift_offset('A' - 'a') @@ -123,13 +126,14 @@ protected: reset_all(); } - virtual void init(int *cursor_pos, vector *sort_list1, vector *sort_list2 = NULL, char select_key = 's') + virtual void init(int *cursor_pos, vector *sort_list1, vector *sort_list2 = NULL, char select_key = 's') { this->cursor_pos = cursor_pos; this->sort_list1 = sort_list1; this->sort_list2 = sort_list2; this->select_key = select_key; select_token = (df::interface_key) (ascii_to_enum_offset + select_key); + track_secondary_values = false; valid = true; } @@ -156,6 +160,43 @@ protected: search_string = ""; saved_list1.clear(); saved_list2.clear(); + reference_list.clear(); + saved_indexes.clear(); + } + + void update_secondary_values() + { + if (sort_list2 != NULL && track_secondary_values) + { + bool list_has_been_sorted = (sort_list1->size() == reference_list.size() + && *sort_list1 != reference_list); + + for (int i = 0; i < saved_indexes.size(); i++) + { + int adjusted_item_index = i; + if (list_has_been_sorted) + { + for (int j = 0; j < sort_list1->size(); j++) + { + if ((*sort_list1)[j] == reference_list[i]) + { + adjusted_item_index = j; + break; + } + } + } + + saved_list2[saved_indexes[i]] = (*sort_list2)[adjusted_item_index]; + } + saved_indexes.clear(); + } + } + + //Used to work out if filtered list has been sorted after filtering + void store_reference_values() + { + if (track_secondary_values) + reference_list = *sort_list1; } void clear_search() @@ -164,8 +205,12 @@ protected: { *sort_list1 = saved_list1; if (sort_list2 != NULL) + { + update_secondary_values(); *sort_list2 = saved_list2; + } } + store_reference_values(); search_string = ""; } @@ -183,23 +228,35 @@ protected: if (sort_list2 != NULL) saved_list2 = *sort_list2; } + else + update_secondary_values(); + sort_list1->clear(); if (sort_list2 != NULL) + { sort_list2->clear(); + saved_indexes.clear(); + } string search_string_l = toLower(search_string); for (int i = 0; i < saved_list1.size(); i++ ) { - T *element = saved_list1[i]; + T element = saved_list1[i]; string desc = toLower(get_element_description(element)); if (desc.find(search_string_l) != string::npos) { sort_list1->push_back(element); if (sort_list2 != NULL) + { sort_list2->push_back(saved_list2[i]); + if (track_secondary_values) + saved_indexes.push_back(i); + } } } + store_reference_values(); + *cursor_pos = 0; } @@ -221,12 +278,12 @@ protected: OutputString(10, x, y, "_"); } - virtual string get_element_description(T *element) const = 0; + virtual string get_element_description(T element) const = 0; virtual void render () const = 0; private: - vector *sort_list1; - vector *sort_list2; + vector *sort_list1; + vector *sort_list2; int *cursor_pos; char select_key; @@ -237,7 +294,6 @@ private: const int shift_offset; }; - template search_parent *search_parent ::lock = NULL; @@ -279,7 +335,7 @@ template V search_hook ::module; // // START: Stocks screen search // -class stocks_search : public search_parent +class stocks_search : public search_parent { public: @@ -354,7 +410,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); // // START: Unit screen search // -class unitlist_search : public search_parent +class unitlist_search : public search_parent { public: @@ -410,7 +466,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); // // START: Trade screen search // -class trade_search_base : public search_parent +class trade_search_base : public search_parent { private: @@ -434,7 +490,8 @@ public: if (!valid) { viewscreen = screen; - search_parent::init(&screen->trader_cursor, &screen->trader_items, NULL, 'q'); + search_parent::init(&screen->trader_cursor, &screen->trader_items, &screen->trader_selected, 'q'); + track_secondary_values = true; } } }; @@ -457,7 +514,8 @@ public: if (!valid) { viewscreen = screen; - search_parent::init(&screen->broker_cursor, &screen->broker_items, NULL, 'w'); + search_parent::init(&screen->broker_cursor, &screen->broker_items, &screen->broker_selected, 'w'); + track_secondary_values = true; } } }; @@ -505,4 +563,4 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch trade_search_fort_hook::module.reset_all(); stocks_search_hook::module.reset_all(); return CR_OK; -} +} \ No newline at end of file From 2fcceaa65e8f3ff72c70af319fff09330b8baa8e Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Mon, 22 Oct 2012 21:44:02 +1300 Subject: [PATCH 35/79] Set priority over manipulator plugin Include animal type is search --- plugins/search.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index 81785d914..6b9a94947 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -7,9 +7,10 @@ #include -#include "df/viewscreen_unitlistst.h" +#include "df/viewscreen_petst.h" #include "df/viewscreen_storesst.h" #include "df/viewscreen_tradegoodsst.h" +#include "df/viewscreen_unitlistst.h" #include "df/interface_key.h" using std::set; @@ -431,12 +432,16 @@ public: private: virtual string get_element_description(df::unit *element) const { - return Translation::TranslateName(Units::getVisibleName(element), false); + string desc = Translation::TranslateName(Units::getVisibleName(element), false); + if (viewscreen->page == 1) + desc += Units::getProfessionName(element); + + return desc; } virtual bool should_check_input(set *input) { - if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) + if (input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT) || input->count(interface_key::CUSTOM_L)) { if (!is_entry_mode()) { @@ -455,14 +460,22 @@ private: }; typedef search_hook unitlist_search_hook; -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(unitlist_search_hook, render); +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, feed, 100); +IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, render, 100); // // END: Unit screen search // +// +// TODO: Animals screen search +// + +// +// END: Animals screen search +// + // // START: Trade screen search // From 94898a73fb0be419c93b2237eae197e34dbea50d Mon Sep 17 00:00:00 2001 From: Anuradha Dissanayake Date: Tue, 23 Oct 2012 21:20:51 +1300 Subject: [PATCH 36/79] Add Search plugin comments --- plugins/search.cpp | 66 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index 6b9a94947..fdc788955 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -1,5 +1,3 @@ -// Dwarf Manipulator - a Therapist-style labor editor - #include #include #include @@ -7,7 +5,7 @@ #include -#include "df/viewscreen_petst.h" +//#include "df/viewscreen_petst.h" #include "df/viewscreen_storesst.h" #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_unitlistst.h" @@ -22,20 +20,35 @@ using namespace df::enums; using df::global::gps; +/* +Search Plugin + +A plugin that adds a "Search" hotkey to some screens (Units, Trade and Stocks) +that allows filtering of the list items by a typed query. + +Works by manipulating the vector(s) that the list based viewscreens use to store +their items. When a search is started the plugin saves the original vectors and +with each keystroke creates a new filtered vector off the saves for the screen +to use. +*/ + + void OutputString(int8_t color, int &x, int y, const std::string &text) { Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); x += text.length(); } - // // START: Base Search functionality // + +// Parent class that does most of the work template class search_parent { public: + // Called each time you enter or leave a searchable screen. Resets everything. void reset_all() { reset_search(); @@ -47,11 +60,15 @@ public: track_secondary_values = false; } + // A new keystroke is received in a searchable screen virtual bool process_input(set *input) { + // If the page has two search options (Trade screen), only allow one to operate + // at a time if (lock != NULL && lock != this) return false; + // Allows custom preprocessing for each screen if (!should_check_input(input)) return false; @@ -59,14 +76,18 @@ public: if (entry_mode) { + // Query typing mode + df::interface_key last_token = *input->rbegin(); if (last_token >= interface_key::STRING_A032 && last_token <= interface_key::STRING_A126) { + // Standard character search_string += last_token - ascii_to_enum_offset; do_search(); } else if (last_token == interface_key::STRING_A000) { + // Backspace if (search_string.length() > 0) { search_string.erase(search_string.length()-1); @@ -75,31 +96,40 @@ public: } else if (input->count(interface_key::SELECT) || input->count(interface_key::LEAVESCREEN)) { + // ENTER or ESC: leave typing mode end_entry_mode(); } else if (input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN) || input->count(interface_key::CURSOR_LEFT) || input->count(interface_key::CURSOR_RIGHT)) { + // Arrow key pressed. Leave entry mode and allow screen to process key end_entry_mode(); key_processed = false; } } + // Not in query typing mode else if (input->count(select_token)) { + // Hotkey pressed, enter typing mode start_entry_mode(); } else if (input->count((df::interface_key) (select_token + shift_offset))) { + // Shift + Hotkey pressed, clear query clear_search(); } else { + // Not a key for us, pass it on to the screen key_processed = false; } - return key_processed || entry_mode; + return key_processed || entry_mode; // Only pass unrecognized keys down if not in typing mode } + // Called if the search should be redone after the screen processes the keystroke. + // Used by the stocks screen where changing categories should redo the search on + // the new category. virtual void do_post_update_check() { if (redo_search) @@ -165,6 +195,10 @@ protected: saved_indexes.clear(); } + // If the second vector is editable (i.e. Trade screen vector used for marking). then it may + // have been edited while the list was filtered. We have to update the original unfiltered + // list with these values. Uses a stored reference vector to determine if the list has been + // reordered after filtering, in which case indexes must be remapped. void update_secondary_values() { if (sort_list2 != NULL && track_secondary_values) @@ -193,13 +227,14 @@ protected: } } - //Used to work out if filtered list has been sorted after filtering + // Store a copy of filtered list, used later to work out if filtered list has been sorted after filtering void store_reference_values() { if (track_secondary_values) reference_list = *sort_list1; } + // Shortcut to clear the search immediately void clear_search() { if (saved_list1.size() > 0) @@ -215,6 +250,7 @@ protected: search_string = ""; } + // The actual sort void do_search() { if (search_string.length() == 0) @@ -225,13 +261,15 @@ protected: if (saved_list1.size() == 0) { + // On first run, save the original list saved_list1 = *sort_list1; if (sort_list2 != NULL) saved_list2 = *sort_list2; } else - update_secondary_values(); + update_secondary_values(); // Update original list with any modified values + // Clear viewscreen vectors sort_list1->clear(); if (sort_list2 != NULL) { @@ -251,12 +289,12 @@ protected: { sort_list2->push_back(saved_list2[i]); if (track_secondary_values) - saved_indexes.push_back(i); + saved_indexes.push_back(i); // Used to map filtered indexes back to original, if needed } } } - store_reference_values(); + store_reference_values(); //Keep a copy, in case user sorts new list *cursor_pos = 0; } @@ -266,6 +304,7 @@ protected: return true; } + // Display hotkey message void print_search_option(int x, int y = -1) const { if (y == -1) @@ -297,7 +336,7 @@ private: }; template search_parent *search_parent ::lock = NULL; - +// Parent struct for the hooks template struct search_hook : T { @@ -355,6 +394,7 @@ public: { if (viewscreen->in_group_mode) { + // Disable search if item lists are grouped clear_search(); reset_search(); } @@ -385,6 +425,7 @@ private: if ((input->count(interface_key::CURSOR_UP) || input->count(interface_key::CURSOR_DOWN)) && !viewscreen->in_right_list) { + // Redo search if category changes saved_list1.clear(); end_entry_mode(); if (search_string.length() > 0) @@ -434,7 +475,7 @@ private: { string desc = Translation::TranslateName(Units::getVisibleName(element), false); if (viewscreen->page == 1) - desc += Units::getProfessionName(element); + desc += Units::getProfessionName(element); // Check animal type too return desc; } @@ -445,11 +486,12 @@ private: { if (!is_entry_mode()) { + // Changing screens, reset search clear_search(); reset_all(); } else - input->clear(); + input->clear(); // Ignore cursor keys when typing return false; } From 209d593f2173e70085fe287d7635c03d66b94cd2 Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 1 Nov 2012 16:00:00 +0200 Subject: [PATCH 37/79] Another day, another commit. --- plugins/Dfusion/luafiles/embark/init.lua | 146 +++++++++++++--------- plugins/lua/dfusion.lua | 19 ++- plugins/lua/dfusion/embark.lua | 148 +++++++++++++++++++++++ plugins/lua/dfusion/embark.o | Bin 0 -> 369 bytes scripts/dfusion.lua | 40 ++++++ 5 files changed, 289 insertions(+), 64 deletions(-) create mode 100644 plugins/lua/dfusion/embark.lua create mode 100644 plugins/lua/dfusion/embark.o diff --git a/plugins/Dfusion/luafiles/embark/init.lua b/plugins/Dfusion/luafiles/embark/init.lua index 76be00c72..b7795d049 100644 --- a/plugins/Dfusion/luafiles/embark/init.lua +++ b/plugins/Dfusion/luafiles/embark/init.lua @@ -2,68 +2,94 @@ local dfu=require("plugins.dfusion") local ms=require("memscan") local MAX_RACES=100 CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin) -CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} -function CustomEmbark:install() - local stoff=dfhack.internal.getAddress('start_dwarf_count') - if #self.race_caste_data<7 then - error("caste and race count must be bigger than 6") +local myos=dfhack.getOSType() +if myos=="windows" then + + CustomEmbark.ATTRS{filename="dfusion/embark/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} + CustomEmbark.class_status="valid, not installed" + function CustomEmbark:install() + local stoff=dfhack.internal.getAddress('start_dwarf_count') + + if #self.race_caste_data<7 then + error("caste and race count must be bigger than 6") + end + if #self.race_caste_data>MAX_RACES then + error("caste and race count must be less then "..MAX_RACES) + end + if stoff==nil then + error("address for start_dwarf_count not found!") + end + local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) + print(string.format("start=%08x",stoff)) + local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id] + local tmp_table=dfu.dwordToTable(race_id_offset) + for k,v in ipairs(tmp_table) do + table.insert(needle,v) + end + + local mem=ms.get_code_segment() + print(mem.uint8_t:addr2idx(stoff)) + print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff))) + local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber + if trg_offset==nil then + error("address for race_load not found") + end + local call_data={0x90,0x90} + local _,data_offset=df.sizeof(self.data) + dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) + self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} + needle={0x83,0xc8,0xff} -- or eax, 0xFF + local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) + if caste_offset==nil or caste_offset-stoff>1000 then + error("Caste change code not found or found too far!") + end + + self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} + self.disable_castes:apply() + self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} + self.dwarfcount:apply() + local caste_array=self:allocate("caste_array","uint16_t",#self.race_caste_data) + local race_array=self:allocate("race_array","uint16_t",#self.race_caste_data) + self:setEmbarkParty(self.race_caste_data) + for k,v in ipairs(self.race_caste_data) do + caste_array[k-1]=v[2] + race_array[k-1]=v[1] + end + local race_array_off,caste_array_off + local _ + _,race_array_off=df.sizeof(race_array) + _,caste_array_off=df.sizeof(caste_array) + self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess... + self:set_marker_dword("caste",race_array_off) + + self:move_to_df() + self.call_patch:apply() + self.installed=true end - if #self.race_caste_data>MAX_RACES then - error("caste and race count must be less then "..MAX_RACES) + function CustomEmbark:setEmbarkParty(racesAndCastes) + self.race_caste_data=racesAndCastes + if self.dwarfcount== nil then + self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} + self.dwarfcount:apply() + else + self.dwarfcount:repatch(dfu.dwordToTable(#self.race_caste_data)) + end + end - if stoff==nil then - error("address for start_dwarf_count not found!") + function CustomEmbark:status() + if self.installed then + return "valid, installed" + else + return "valid, not installed" + end end - local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) - print(string.format("start=%08x",stoff)) - local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id] - local tmp_table=dfu.dwordToTable(race_id_offset) - for k,v in ipairs(tmp_table) do - table.insert(needle,v) - end - - local mem=ms.get_code_segment() - print(mem.uint8_t:addr2idx(stoff)) - print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff))) - local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber - if trg_offset==nil then - error("address for race_load not found") - end - local call_data={0x90,0x90} - local _,data_offset=df.sizeof(self.data) - dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) - self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} - needle={0x83,0xc8,0xff} -- or eax, 0xFF - local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) - if caste_offset==nil or caste_offset-stoff>1000 then - error("Caste change code not found or found too far!") - end - - self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} - self.disable_castes:apply() - self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} - self.dwarfcount:apply() - local caste_array=self:allocate("caste_array","uint16_t",#self.race_caste_data) - local race_array=self:allocate("race_array","uint16_t",#self.race_caste_data) - for k,v in ipairs(self.race_caste_data) do - caste_array[k-1]=v[2] - race_array[k-1]=v[1] - end - local race_array_off,caste_array_off - local _ - _,race_array_off=df.sizeof(race_array) - _,caste_array_off=df.sizeof(caste_array) - self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess... - self:set_marker_dword("caste",race_array_off) - - self:move_to_df() - self.call_patch:apply() - self.installed=true -end -function CustomEmbark:uninstall() - if self.installed then - self.call_patch:remove() - self.disable_castes:remove() - self.dwarfcount:remove() + function CustomEmbark:uninstall() + if self.installed then + self.call_patch:remove() + self.disable_castes:remove() + self.dwarfcount:remove() + end end +else + CustomEmbark.class_status="invalid, os not supported" end \ No newline at end of file diff --git a/plugins/lua/dfusion.lua b/plugins/lua/dfusion.lua index ac9a20868..053c486cf 100644 --- a/plugins/lua/dfusion.lua +++ b/plugins/lua/dfusion.lua @@ -107,6 +107,7 @@ end -- A binary hack (obj file) loader/manager -- has to have: a way to get offsets for marked areas (for post load modification) or some way to do that pre-load -- page managing (including excecute/write flags for DEP and the like) +-- TODO plugin state enum, a way to modify post install (could include repatching code...) plugins=plugins or {} BinaryPlugin=defclass(BinaryPlugin) BinaryPlugin.ATTRS {filename=DEFAULT_NIL,reloc_table={},name=DEFAULT_NIL} @@ -115,14 +116,21 @@ function BinaryPlugin:init(args) end function BinaryPlugin:postinit(args) if self.name==nil then error("Not a valid plugin name!") end - --if plugins[args.name]==nil then + if plugins[args.name]==nil then plugins[self.name]=self - --else - -- error("Trying to create a same plugin") - --end + else + error("Trying to create a same plugin") + end self.allocated_object={} self:load() end +function BinaryPlugin:get_or_alloc(name,typename,arrsize) + if self.allocated_object[name]~=nil then + return self.allocated_object[name] + else + return self:allocate(name,typename,arrsize) + end +end function BinaryPlugin:allocate(name,typename,arrsize) local trg if df[typename]==nil then @@ -182,6 +190,9 @@ function BinaryPlugin:print_data() end print(out) end +function BinaryPlugin:status() + return "invalid, base class only!" +end function BinaryPlugin:__gc() for k,v in pairs(self.allocated_object) do df.delete(v) diff --git a/plugins/lua/dfusion/embark.lua b/plugins/lua/dfusion/embark.lua new file mode 100644 index 000000000..a674a96d3 --- /dev/null +++ b/plugins/lua/dfusion/embark.lua @@ -0,0 +1,148 @@ +local _ENV = mkmodule('plugins.dfusion.embark') +local dfu=require("plugins.dfusion") +local ms=require("memscan") +local MAX_RACES=100 +CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin) +CustomEmbark.name="CustomEmbark" +local myos=dfhack.getOSType() +if myos=="windows" then + CustomEmbark.ATTRS{filename="hack/lua/plugins/dfusion/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} + CustomEmbark.class_status="valid, not installed" + function CustomEmbark:install() + local stoff=dfhack.internal.getAddress('start_dwarf_count') + + if #self.race_caste_data<7 then + error("caste and race count must be bigger than 6") + end + if #self.race_caste_data>MAX_RACES then + error("caste and race count must be less then "..MAX_RACES) + end + if stoff==nil then + error("address for start_dwarf_count not found!") + end + local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) + print(string.format("start=%08x",stoff)) + local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id] + local tmp_table=dfu.dwordToTable(race_id_offset) + for k,v in ipairs(tmp_table) do + table.insert(needle,v) + end + + local mem=ms.get_code_segment() + print(mem.uint8_t:addr2idx(stoff)) + print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff))) + local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber + if trg_offset==nil then + error("address for race_load not found") + end + local call_data={0x90,0x90} + local _,data_offset=df.sizeof(self.data) + dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) + self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} + needle={0x83,0xc8,0xff} -- or eax, 0xFF + local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) + if caste_offset==nil or caste_offset-stoff>1000 then + error("Caste change code not found or found too far!") + end + + self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} + self.disable_castes:apply() + + + self:setEmbarkParty(self.race_caste_data) + local caste_array=self:get_or_alloc("caste_array","uint16_t",MAX_RACES) + local race_array=self:get_or_alloc("race_array","uint16_t",MAX_RACES) + + local race_array_off,caste_array_off + local _ + _,race_array_off=df.sizeof(race_array) + _,caste_array_off=df.sizeof(caste_array) + self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess... + self:set_marker_dword("caste",race_array_off) + + self:move_to_df() + self.call_patch:apply() + self.installed=true + end + function CustomEmbark:setEmbarkParty(racesAndCastes) + local stoff=dfhack.internal.getAddress('start_dwarf_count') + if #racesAndCastes<7 then + error("caste and race count must be bigger than 6") + end + if #racesAndCastes>MAX_RACES then + error("caste and race count must be less then "..MAX_RACES) + end + + self.race_caste_data=racesAndCastes + if self.dwarfcount== nil then + self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} + self.dwarfcount:apply() + else + self.dwarfcount:repatch(dfu.dwordToTable(#self.race_caste_data)) + end + local caste_array=self:get_or_alloc("caste_array","uint16_t",MAX_RACES) + local race_array=self:get_or_alloc("race_array","uint16_t",MAX_RACES) + for k,v in ipairs(self.race_caste_data) do + caste_array[k-1]=v[2] + race_array[k-1]=v[1] + end + end + function CustomEmbark:status() + if self.installed then + return "valid, installed" + else + return "valid, not installed" + end + end + function CustomEmbark:uninstall() + if self.installed then + self.call_patch:remove() + self.disable_castes:remove() + self.dwarfcount:remove() + end + end + function CustomEmbark:edit() + local data=self.race_caste_data or {} + print(string.format("Old race count:%d",#data)) + local endthis=false + print("current:") + while(not endthis) do + print(" # RaceId Race name Caste num") + for k,v in pairs(data) do + local name=df.creature_raw.find(v[1]).creature_id or "" + print(string.format("%3d. %6d %20s %d",k,v[1],name,v[2])) + end + print("a- add, r-remove, c-cancel, s-save and update") + local choice=io.stdin:read() + if choice=='a' then + print("Enter new race then caste ids:") + local race=tonumber(io.stdin:read()) + local caste=tonumber(io.stdin:read()) + if race and caste then + table.insert(data,{race,caste}) + else + print("input parse error") + end + elseif choice=='r' then + print("enter number to remove:") + local num_rem=tonumber(io.stdin:read()) + if num_rem~=nil then + table.remove(data,num_rem) + end + elseif choice=='c' then + endthis=true + elseif choice=='s' then + endthis=true + if self.installed then + self:setEmbarkParty(data) + else + self.race_caste_data=data + self:install() + end + end + end + end +else + CustomEmbark.class_status="invalid, os not supported" +end +return _ENV \ No newline at end of file diff --git a/plugins/lua/dfusion/embark.o b/plugins/lua/dfusion/embark.o new file mode 100644 index 0000000000000000000000000000000000000000..87f5bbd68f5c8d0aaf6f81485f653a440145a952 GIT binary patch literal 369 zcmeZaWM%+?B|yvtX0bBrm84dbfY}g20!Z~B@j*-l27?5>l*E!mG;wsU1B1Z Date: Fri, 2 Nov 2012 00:28:16 +0200 Subject: [PATCH 38/79] New way of doing things! Now using a class for menus, also no (non script) way to use bin-plugins. --- plugins/Dfusion/luafiles/adv_tools/init.lua | 133 -------------------- plugins/Dfusion/luafiles/common.lua | 32 +---- plugins/lua/dfusion.lua | 35 ++++++ plugins/lua/dfusion/adv_tools.lua | 52 ++++++++ scripts/dfusion.lua | 47 ++----- 5 files changed, 96 insertions(+), 203 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/adv_tools/init.lua create mode 100644 plugins/lua/dfusion/adv_tools.lua diff --git a/plugins/Dfusion/luafiles/adv_tools/init.lua b/plugins/Dfusion/luafiles/adv_tools/init.lua deleted file mode 100644 index 566484f01..000000000 --- a/plugins/Dfusion/luafiles/adv_tools/init.lua +++ /dev/null @@ -1,133 +0,0 @@ -adv_tools= {} -adv_tools.menu=MakeMenu() ---TODO make every tool generic (work for both modes) -function adv_tools.reincarnate(swap_soul) --only for adventurer i guess - if swap_soul==nil then - swap_soul=true - end - local adv=df.global.world.units.active[0] - if adv.flags1.dead==false then - error("You are not dead (yet)!") - end - local hist_fig=getNemesis(adv).figure - if hist_fig==nil then - error("No historical figure for adventurer...") - end - local events=df.global.world.history.events - local trg_hist_fig - for i=#events-1,0,-1 do -- reverse search because almost always it will be last entry - if df.history_event_hist_figure_diedst:is_instance(events[i]) then - --print("is instance:"..i) - if events[i].victim==hist_fig.id then - --print("Is same id:"..i) - trg_hist_fig=events[i].slayer - if trg_hist_fig then - trg_hist_fig=df.historical_figure.find(trg_hist_fig) - end - break - end - end - end - if trg_hist_fig ==nil then - qerror("Slayer not found") - end - - local trg_unit=trg_hist_fig.unit_id - if trg_unit==nil then - qerror("Unit id not found!") - end - local trg_unit_final=df.unit.find(trg_unit) - - tools.change_adv(trg_unit_final) - if swap_soul then --actually add a soul... - t_soul=adv.status.current_soul - adv.status.current_soul=df.NULL - adv.status.souls:resize(0) - trg_unit_final.status.current_soul=t_soul - trg_unit_final.status.souls:insert(#trg_unit_final.status.souls,t_soul) - end -end -adv_tools.menu:add("Reincarnate",adv_tools.reincarnate) -function adv_tools.ressurect() - - v2=engine.peek(vector:getval(indx),ptr_Creature.hurt1) - for i=0,v2:size()-1 do - v2:setval(i,0) - end - v2=engine.peek(vector:getval(indx),ptr_Creature.hurt2) - v2.type=DWORD - for i=0,v2:size()-1 do - v2:setval(i,0) - end - engine.poke(vector:getval(indx),ptr_Creature.bloodlvl,60000) --give blood - engine.poke(vector:getval(indx),ptr_Creature.bleedlvl,0) --stop some bleeding... - local flg=engine.peek(vector:getval(indx),ptr_Creature.flags) - flg:set(1,false) --ALIVE - flg:set(39,false) -- leave body yet again - flg:set(37,false) -- something todo with wounds- lets you walk again. - flg:set(58,true) -- makes them able to breathe - flg:set(61,true) -- gives them sight - engine.poke(vector:getval(indx),ptr_Creature.flags,flg) -end - -function adv_tools.wagonmode() --by rumrusher - --first three lines same as before (because we will need an offset of creature at location x,y,z) - myoff=offsets.getEx("AdvCreatureVec") - vector=engine.peek(myoff,ptr_vector) - indx=GetCreatureAtPos(getxyz()) - --indx=0 - --print(string.format("%x",vector:getval(indx))) - flg=engine.peek(vector:getval(indx),ptr_Creature.flags) --get flags - flg:set(1,false) - flg:set(74,false) - engine.poke(vector:getval(indx),ptr_Creature.flags,flg) - print("To stay normal press y, else hit Enter turn Wagon mode on.") - r=io.stdin:read() -- repeat for it too work... also creature will be dead. - if r== "y" then - flg=engine.peek(vector:getval(indx),ptr_Creature.flags) - flg:set(1,false) - engine.poke(vector:getval(indx),ptr_Creature.flags,flg) - else - flg=engine.peek(vector:getval(indx),ptr_Creature.flags) - flg:set(1,false) - flg:flip(74) - engine.poke(vector:getval(indx),ptr_Creature.flags,flg) - end -end -function selectall() - local retvec={} --return vector (or a list) - myoff=offsets.getEx("AdvCreatureVec") - vector=engine.peek(myoff,ptr_vector) --standart start - for i=0,vector:size()-1 do --check all creatures - local off - off=vector:getval(i) - local flags=engine.peek(off,ptr_Creature.flags) - if flags:get(1)==true then --if dead ... - table.insert(retvec,off)--... add it to return vector - end - end - return retvec --return the "return vector" :) -end -function adv_tools.hostilate() - vector=engine.peek(offsets.getEx("AdvCreatureVec"),ptr_vector) - id=GetCreatureAtPos(getxyz()) - print(string.format("Vec:%d cr:%d",vector:size(),id)) - off=vector:getval(id) - crciv=engine.peek(vector:getval(id),ptr_Creature.civ) - curciv=engine.peek(vector:getval(0),ptr_Creature.civ) - - if curciv==crciv then - print("Friendly-making enemy") - engine.poke(off,ptr_Creature.civ,-1) - flg=engine.peek(off,ptr_Creature.flags) - flg:set(17,true) - engine.poke(off,ptr_Creature.flags,flg) - else - print("Enemy- making friendly") - engine.poke(off,ptr_Creature.civ,curciv) - flg=engine.peek(off,ptr_Creature.flags) - flg:set(17,false) - flg:set(19,false) - engine.poke(off,ptr_Creature.flags,flg) - end -end diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index a6781b385..bdf3a2bc8 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -242,37 +242,7 @@ function engine.installMod(file,name,bonussize) return T end -it_menu={} -it_menu.__index=it_menu -function it_menu:add(name,func) - table.insert(self.items,{func,name}) -end -function it_menu:display() - print("Select choice (q exits):") - for p,c in pairs(self.items) do - print(string.format("%3d).%s",p,c[2])) - end - local ans - repeat - local r - r=getline("") - if r==nil then return end - if r=='q' then return end - ans=tonumber(r) - - if ans==nil or not(ans<=#self.items and ans>0) then - print("incorrect choice") - end - - until ans~=nil and (ans<=#self.items and ans>0) - self.items[ans][1]() -end -function MakeMenu() - local ret={} - ret.items={} - setmetatable(ret,it_menu) - return ret -end + function PrintPattern(loadedpattern) for k,v in pairs(loadedpattern) do diff --git a/plugins/lua/dfusion.lua b/plugins/lua/dfusion.lua index 053c486cf..c7bd9bef3 100644 --- a/plugins/lua/dfusion.lua +++ b/plugins/lua/dfusion.lua @@ -202,4 +202,39 @@ function BinaryPlugin:__gc() end self.data:delete() end +-- a Menu for some stuff. Maybe add a posibility of it working as a gui, or a gui adaptor? +-- Todo add hints, and parse them to make a "smart" choice of parameters to pass +SimpleMenu=defclass(SimpleMenu) +SimpleMenu.ATTRS{title=DEFAULT_NIL} +function SimpleMenu:init(args) + self.items={} +end +function SimpleMenu:add(name,entry,hints) + table.insert(self.items,{entry,name,hints}) +end +function SimpleMenu:display() + print("Select choice (q exits):") + for p,c in pairs(self.items) do + print(string.format("%3d).%s",p,c[2])) + end + local ans + repeat + local r + r=io.stdin:read() + if r==nil then return end + if r=='q' then return end + ans=tonumber(r) + + if ans==nil or not(ans<=#self.items and ans>0) then + print("Invalid choice.") + end + + until ans~=nil and (ans<=#self.items and ans>0) + if type(self.items[ans][1])=="function" then + self.items[ans][1]() + else + self.items[ans][1]:display() + end +end + return _ENV \ No newline at end of file diff --git a/plugins/lua/dfusion/adv_tools.lua b/plugins/lua/dfusion/adv_tools.lua new file mode 100644 index 000000000..4440a4b2f --- /dev/null +++ b/plugins/lua/dfusion/adv_tools.lua @@ -0,0 +1,52 @@ +local _ENV = mkmodule('plugins.dfusion.adv_tools') +local dfu=require("plugins.dfusion") +menu=dfu.SimpleMenu() +function Reincarnate(trg_unit,swap_soul) --only for adventurer i guess + if swap_soul==nil then + swap_soul=true + end + local adv=trg_unit or df.global.world.units.active[0] + if adv.flags1.dead==false then + qerror("You are not dead (yet)!") + end + local hist_fig=getNemesis(adv).figure + if hist_fig==nil then + qerror("No historical figure for adventurer...") + end + local events=df.global.world.history.events + local trg_hist_fig + for i=#events-1,0,-1 do -- reverse search because almost always it will be last entry + if df.history_event_hist_figure_diedst:is_instance(events[i]) then + --print("is instance:"..i) + if events[i].victim==hist_fig.id then + --print("Is same id:"..i) + trg_hist_fig=events[i].slayer + if trg_hist_fig then + trg_hist_fig=df.historical_figure.find(trg_hist_fig) + end + break + end + end + end + if trg_hist_fig ==nil then + qerror("Slayer not found") + end + + local trg_unit=trg_hist_fig.unit_id + if trg_unit==nil then + qerror("Unit id not found!") + end + local trg_unit_final=df.unit.find(trg_unit) + + tools.change_adv(trg_unit_final) + if swap_soul then --actually add a soul... + t_soul=adv.status.current_soul + adv.status.current_soul=df.NULL + adv.status.souls:resize(0) + trg_unit_final.status.current_soul=t_soul + trg_unit_final.status.souls:insert(#trg_unit_final.status.souls,t_soul) + end +end +menu:add("Reincarnate",Reincarnate,{{df.unit,"optional"}})-- bool, optional + +return _ENV \ No newline at end of file diff --git a/scripts/dfusion.lua b/scripts/dfusion.lua index 63a530454..4fee0438e 100644 --- a/scripts/dfusion.lua +++ b/scripts/dfusion.lua @@ -1,42 +1,11 @@ --- a binary hack/plugin collection for df +-- a collection of misc lua scripts local dfu=require("plugins.dfusion") local myos=dfhack.getOSType() - ---some imports go here -local plugins={ - require("plugins.dfusion.embark").CustomEmbark -} ---show a table of all the statuses -function status(plug) - if dfu.plugins[plug.name]==nil then - return plug.class_status - else - return dfu.plugins[plug.name]:status() - end +args={...} +mainmenu=dfu.SimpleMenu() +function runsave() + print("doing file:"..df.global.world.cur_savegame.save_dir) end -function printPlugs() - local endthis=false - print("current:") - while(not endthis) do - for k,v in pairs(plugins) do - if v then - print(string.format("%2d. %15s-%s",k,v.name,status(v))) - end - end - print("e-edit and load, u-unload,c-cancel and then number to manipulate:") - local choice=io.stdin:read() - local num=tonumber(io.stdin:read()) - if num then - local plg=dfu.plugins[plugins[num].name] or plugins[num]() - if choice=='e' then - plg:edit() - elseif choice=='u' then - plg:uninstall() - elseif choice=='c' then - endthis=true - end - end - end -end - -printPlugs() \ No newline at end of file +mainmenu:add("Run save script",runsave) +mainmenu:add("Adventurer tools",require("plugins.dfusion.adv_tools").menu) +mainmenu:display() \ No newline at end of file From 296d1cf090745c9bb667ddb8d30395ad431d13ef Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 2 Nov 2012 00:50:20 +0200 Subject: [PATCH 39/79] More scripts for dfusion. Only fixes left, and updating bin-plugins (friendship and migrants(??)) --- plugins/lua/dfusion/adv_tools.lua | 68 ++++++++++++- plugins/lua/dfusion/tools.lua | 164 ++++++++++++++++++++++++++++++ scripts/dfusion.lua | 5 +- 3 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 plugins/lua/dfusion/tools.lua diff --git a/plugins/lua/dfusion/adv_tools.lua b/plugins/lua/dfusion/adv_tools.lua index 4440a4b2f..31e83b234 100644 --- a/plugins/lua/dfusion/adv_tools.lua +++ b/plugins/lua/dfusion/adv_tools.lua @@ -48,5 +48,69 @@ function Reincarnate(trg_unit,swap_soul) --only for adventurer i guess end end menu:add("Reincarnate",Reincarnate,{{df.unit,"optional"}})-- bool, optional - -return _ENV \ No newline at end of file +function change_adv(unit,nemesis) + if nemesis==nil then + nemesis=true --default value is nemesis switch too. + end + if unit==nil then + unit=getCreatureAtPointer() + end + if unit==nil then + error("Invalid unit!") + end + local other=df.global.world.units.active + local unit_indx + for k,v in pairs(other) do + if v==unit then + unit_indx=k + break + end + end + if unit_indx==nil then + error("Unit not found in array?!") --should not happen + end + other[unit_indx]=other[0] + other[0]=unit + if nemesis then --basicly copied from advtools plugin... + local nem=getNemesis(unit) + local other_nem=getNemesis(other[unit_indx]) + if other_nem then + other_nem.flags[0]=false + other_nem.flags[1]=true + end + if nem then + nem.flags[0]=true + nem.flags[2]=true + for k,v in pairs(df.global.world.nemesis.all) do + if v.id==nem.id then + df.global.ui_advmode.player_id=k + end + end + else + error("Current unit does not have nemesis record, further working not guaranteed") + end + end +end +menu:add("Change adventurer",change_adv) +function log_pos() + local adv=df.global.world.units.active[0] + + local wmap=df.global.world.map + local sub_pos={x=adv.pos.x,y=adv.pos.y,z=adv.pos.z} + local region_pos={x=wmap.region_x,y=wmap.region_y,z=wmap.region_z} + local pos={x=sub_pos.x+region_pos.x*48,y=sub_pos.y+region_pos.y*48,z=sub_pos.z+region_pos.z} + local state + if adv.flags1.dead then + state="dead" + else + state="live n kicking" + end + local message=string.format("%s %s at pos={%d,%d,%d} region={%d,%d,%d}",dfhack.TranslateName(adv.name),state,pos.x,pos.y,pos.z,region_pos.x,region_pos.y,region_pos.z) + print(message) + local path="deaths_"..df.global.world.cur_savegame.save_dir..".txt" + local f=io.open(path,"a") + f:write(message) + f:close() +end +menu:add("Log adventurers position",log_pos) +return _ENV diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua new file mode 100644 index 000000000..9ddfb8e45 --- /dev/null +++ b/plugins/lua/dfusion/tools.lua @@ -0,0 +1,164 @@ +local _ENV = mkmodule('plugins.dfusion.tools') +local dfu=require("plugins.dfusion") +local ms=require "memscan" +menu=dfu.SimpleMenu() +function setrace(name) --TODO FIX + RaceTable=BuildNameTable() + print("Your current race is:"..GetRaceToken(df.global.ui.race_id)) + local id + if name == nil then + print("Type new race's token name in full caps (q to quit):") + repeat + entry=getline() + if entry=="q" then + return + end + id=RaceTable[entry] + until id~=nil + else + id=RaceTable[name] + if id==nil then + error("Name not found!") + end + end + df.global.ui.race_id=id +end +menu:add("Set current race",setrace) +function GiveSentience(names) --TODO FIX + RaceTable=RaceTable or BuildNameTable() --slow.If loaded don't load again + if names ==nil then + ids={} + print("Type race's token name in full caps to give sentience to:") + repeat + entry=getline() + id=RaceTable[entry] + until id~=nil + table.insert(ids,id) + else + ids={} + for _,name in pairs(names) do + id=RaceTable[name] + table.insert(ids,id) + end + end + for _,id in pairs(ids) do + local races=df.global.world.raws.creatures.all + + local castes=races[id].caste + print(string.format("Caste count:%i",castes.size)) + for i =0,#castes-1 do + + print("Caste name:"..castes[i].caste_id.."...") + + local flags=castes[i].flags + --print(string.format("%x",flagoffset)) + if flags.CAN_SPEAK then + print("\tis sentient.") + else + print("\tnon sentient. Allocating IQ...") + flags.CAN_SPEAK=true + end + end + end +end +menu:add("Give Sentience",GiveSentience) +function MakeFollow(unit,trgunit) + if unit == nil then + unit=dfhack.gui.getSelectedUnit() + end + if unit== nil then + error("Invalid creature") + end + if trgunit==nil then + trgunit=df.global.world.units.active[0] + end + unit.relations.group_leader_id=trgunit.id + local u_nem=getNemesis(unit) + local t_nem=getNemesis(trgunit) + if u_nem then + u_nem.group_leader_id=t_nem.id + end + if t_nem and u_nem then + t_nem.companions:insert(#t_nem.companions,u_nem.id) + end +end +menu:add("Make creature follow",MakeFollow) +function project(unit,trg) --TODO add to menu? + if unit==nil then + unit=getCreatureAtPointer() + end + + if unit==nil then + error("Failed to project unit. Unit not selected/valid") + end + -- todo: add projectile to world, point to unit, add flag to unit, add gen-ref to projectile. + local p=df.proj_unitst:new() + local startpos={x=unit.pos.x,y=unit.pos.y,z=unit.pos.z} + p.origin_pos=startpos + p.target_pos=trg + p.cur_pos=startpos + p.prev_pos=startpos + p.unit=unit + --- wtf stuff + p.unk14=100 + p.unk16=-1 + p.unk23=-1 + p.fall_delay=5 + p.fall_counter=5 + p.collided=true + -- end wtf + local citem=df.global.world.proj_list + local maxid=1 + local newlink=df.proj_list_link:new() + newlink.item=p + while citem.item~= nil do + if citem.item.id>maxid then maxid=citem.item.id end + if citem.next ~= nil then + citem=citem.next + else + break + end + end + p.id=maxid+1 + newlink.prev=citem + citem.next=newlink + local proj_ref=df.general_ref_projectile:new() + proj_ref.projectile_id=p.id + unit.refs:insert(#unit.refs,proj_ref) + unit.flags1.projectile=true +end +function empregnate(unit) + if unit==nil then + unit=getSelectedUnit() + end + + if unit==nil then + unit=getCreatureAtPos(getxyz()) + end + + if unit==nil then + error("Failed to empregnate. Unit not selected/valid") + end + if unit.curse then + unit.curse.add_tags2.STERILE=false + end + local genes = unit.appearance.genes + if unit.relations.pregnancy_ptr == nil then + print("creating preg ptr.") + if false then + print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_ptr")))) + return + end + unit.relations.pregnancy_ptr = { new = true, assign = genes } + end + local ngenes = unit.relations.pregnancy_ptr + if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then + print("Array sizes incorrect, fixing.") + ngenes:assign(genes); + end + print("Setting preg timer.") + unit.relations.pregnancy_timer=10 + unit.relations.pregnancy_mystery=1 +end +menu:add("Empregnate",empregnate) +return _ENV \ No newline at end of file diff --git a/scripts/dfusion.lua b/scripts/dfusion.lua index 4fee0438e..79f9fd953 100644 --- a/scripts/dfusion.lua +++ b/scripts/dfusion.lua @@ -4,8 +4,11 @@ local myos=dfhack.getOSType() args={...} mainmenu=dfu.SimpleMenu() function runsave() - print("doing file:"..df.global.world.cur_savegame.save_dir) + local path=string.format("data/save/%s/dfhack.lua",df.global.world.cur_savegame.save_dir) + print("doing file:"..path) + loadfile(path)() end mainmenu:add("Run save script",runsave) mainmenu:add("Adventurer tools",require("plugins.dfusion.adv_tools").menu) +mainmenu:add("Misc tools",require("plugins.dfusion.tools").menu) mainmenu:display() \ No newline at end of file From 5295be5fdbe4ef7e40e174ddfab86253d6d154bd Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 2 Nov 2012 20:28:08 +0200 Subject: [PATCH 40/79] More work done. Only bin-plugs left (and docs) --- plugins/lua/dfusion/adv_tools.lua | 2 +- plugins/lua/dfusion/tools.lua | 41 +++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/plugins/lua/dfusion/adv_tools.lua b/plugins/lua/dfusion/adv_tools.lua index 31e83b234..6e95d2117 100644 --- a/plugins/lua/dfusion/adv_tools.lua +++ b/plugins/lua/dfusion/adv_tools.lua @@ -87,7 +87,7 @@ function change_adv(unit,nemesis) end end else - error("Current unit does not have nemesis record, further working not guaranteed") + qerror("Current unit does not have nemesis record, further working not guaranteed") end end end diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua index 9ddfb8e45..e577a9418 100644 --- a/plugins/lua/dfusion/tools.lua +++ b/plugins/lua/dfusion/tools.lua @@ -2,14 +2,31 @@ local _ENV = mkmodule('plugins.dfusion.tools') local dfu=require("plugins.dfusion") local ms=require "memscan" menu=dfu.SimpleMenu() -function setrace(name) --TODO FIX - RaceTable=BuildNameTable() - print("Your current race is:"..GetRaceToken(df.global.ui.race_id)) +RaceNames={} +function build_race_names() + if #RaceNames~=0 then + return RaceNames + else + for k,v in pairs(df.global.world.raws.creatures.all) do + RaceNames[v.creature_id]=k + end + dfhack.onStateChange.invalidate_races=function(change_id) --todo does this work? + if change_id==SC_WORLD_UNLOADED then + dfhack.onStateChange.invalidate_races=nil + RaceNames={} + end + end + return RaceNames + end +end +function setrace(name) + local RaceTable=build_race_names() + print("Your current race is:"..df.global.world.raws.creatures.all[df.global.ui.race_id].creature_id) local id if name == nil then print("Type new race's token name in full caps (q to quit):") repeat - entry=getline() + local entry=io.stdin:read() if entry=="q" then return end @@ -24,16 +41,20 @@ function setrace(name) --TODO FIX df.global.ui.race_id=id end menu:add("Set current race",setrace) -function GiveSentience(names) --TODO FIX - RaceTable=RaceTable or BuildNameTable() --slow.If loaded don't load again +function GiveSentience(names) + local RaceTable=build_race_names() --slow.If loaded don't load again + local id,ids if names ==nil then ids={} print("Type race's token name in full caps to give sentience to:") repeat - entry=getline() + id=io.stdin:read() id=RaceTable[entry] - until id~=nil - table.insert(ids,id) + if id~=nil then + table.insert(ids,id) + end + until id==nil + else ids={} for _,name in pairs(names) do @@ -45,7 +66,7 @@ function GiveSentience(names) --TODO FIX local races=df.global.world.raws.creatures.all local castes=races[id].caste - print(string.format("Caste count:%i",castes.size)) + print(string.format("Caste count:%i",#castes)) for i =0,#castes-1 do print("Caste name:"..castes[i].caste_id.."...") From 86e4a42bdda70a47338969aba2b8ade42800294f Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 2 Nov 2012 20:59:05 +0200 Subject: [PATCH 41/79] Small fix due to vmethod change --- plugins/reactionhooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/reactionhooks.cpp b/plugins/reactionhooks.cpp index d70fb9ea1..ba0f8742d 100644 --- a/plugins/reactionhooks.cpp +++ b/plugins/reactionhooks.cpp @@ -203,7 +203,7 @@ struct product_hook : item_product { (df::unit *unit, std::vector *out_items, std::vector *in_reag, std::vector *in_items, - int32_t quantity, int16_t skill, + int32_t quantity, df::job_skill skill, df::historical_entity *entity, df::world_site *site) ) { if (auto product = products[this]) From e887c60e93c819dc3b803457dbb6b3c3c7300731 Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 2 Nov 2012 21:00:35 +0200 Subject: [PATCH 42/79] Removed unused buffers. --- plugins/devel/memview.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/devel/memview.cpp b/plugins/devel/memview.cpp index 757b475dd..29b8403f8 100644 --- a/plugins/devel/memview.cpp +++ b/plugins/devel/memview.cpp @@ -168,8 +168,6 @@ command_result memview (color_ostream &out, vector & parameters) else memdata.refresh=0; - - uint8_t *buf,*lbuf; memdata.buf=new uint8_t[memdata.len]; memdata.lbuf=new uint8_t[memdata.len]; Core::getInstance().p->getMemRanges(memdata.ranges); From 3257eb80a1104599b0ae57ccf3a09b38343242d9 Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 2 Nov 2012 16:28:48 -0500 Subject: [PATCH 43/79] Add checks to avoid crashing if we encounter a soulless unit --- plugins/manipulator.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index b6d30ab1e..1bc0195b6 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -298,6 +298,10 @@ bool sortBySkill (const UnitInfo *d1, const UnitInfo *d2) { if (sort_skill != job_skill::NONE) { + if (!d1->unit->status.current_soul) + return !descending; + if (!d2->unit->status.current_soul) + return descending; df::unit_skill *s1 = binsearch_in_vector(d1->unit->status.current_soul->skills, &df::unit_skill::id, sort_skill); df::unit_skill *s2 = binsearch_in_vector(d2->unit->status.current_soul->skills, &df::unit_skill::id, sort_skill); int l1 = s1 ? s1->rating : 0; @@ -1030,7 +1034,9 @@ void viewscreen_unitlaborsst::render() fg = 9; if (columns[col_offset].skill != job_skill::NONE) { - df::unit_skill *skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, columns[col_offset].skill); + df::unit_skill *skill = NULL; + if (unit->status.current_soul) + skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, columns[col_offset].skill); if ((skill != NULL) && (skill->rating || skill->experience)) { int level = skill->rating; @@ -1086,7 +1092,9 @@ void viewscreen_unitlaborsst::render() } else { - df::unit_skill *skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, columns[sel_column].skill); + df::unit_skill *skill = NULL; + if (unit->status.current_soul) + skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, columns[sel_column].skill); if (skill) { int level = skill->rating; From 8bccfb1e9a197084ff645b05ee877209a2475556 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 3 Nov 2012 14:31:07 +0400 Subject: [PATCH 44/79] Fix gcc compiler errors and warnings in search, reindent plugin_init, etc. --- NEWS | 3 +++ plugins/search.cpp | 53 +++++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 0f9bf4c3d..d5080b05a 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,9 @@ DFHack future Together with a couple of binary patches and the gui/assign-rack script, this plugin makes weapon racks, armor stands, chests and cabinets in properly designated barracks be used again for storage of squad equipment. + New Search plugin by falconne: + Adds an incremental search function to the Stocks, Trading and Unit List screens. + DFHack v0.34.11-r2 diff --git a/plugins/search.cpp b/plugins/search.cpp index fdc788955..e19c2e3c0 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -206,12 +206,12 @@ protected: bool list_has_been_sorted = (sort_list1->size() == reference_list.size() && *sort_list1 != reference_list); - for (int i = 0; i < saved_indexes.size(); i++) + for (size_t i = 0; i < saved_indexes.size(); i++) { int adjusted_item_index = i; if (list_has_been_sorted) { - for (int j = 0; j < sort_list1->size(); j++) + for (size_t j = 0; j < sort_list1->size(); j++) { if ((*sort_list1)[j] == reference_list[i]) { @@ -278,7 +278,7 @@ protected: } string search_string_l = toLower(search_string); - for (int i = 0; i < saved_list1.size(); i++ ) + for (size_t i = 0; i < saved_list1.size(); i++ ) { T element = saved_list1[i]; string desc = toLower(get_element_description(element)); @@ -440,8 +440,8 @@ private: typedef search_hook stocks_search_hook; -IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); +template<> IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, feed); +template<> IMPLEMENT_VMETHOD_INTERPOSE(stocks_search_hook, render); // // END: Stocks screen search @@ -502,8 +502,8 @@ private: }; typedef search_hook unitlist_search_hook; -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, feed, 100); -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, render, 100); +template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, feed, 100); +template<> IMPLEMENT_VMETHOD_INTERPOSE_PRIO(unitlist_search_hook, render, 100); // // END: Unit screen search @@ -552,8 +552,8 @@ public: }; typedef search_hook trade_search_merc_hook; -IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, render); +template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, feed); +template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_merc_hook, render); class trade_search_fort : public trade_search_base @@ -576,8 +576,8 @@ public: }; typedef search_hook trade_search_fort_hook; -IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, feed); -IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, render); +template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, feed); +template<> IMPLEMENT_VMETHOD_INTERPOSE(trade_search_fort_hook, render); // // END: Trade screen search @@ -589,10 +589,15 @@ DFHACK_PLUGIN("search"); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { - if (!gps || !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_search_hook, render).apply() - || !INTERPOSE_HOOK(trade_search_merc_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_merc_hook, render).apply() - || !INTERPOSE_HOOK(trade_search_fort_hook, feed).apply() || !INTERPOSE_HOOK(trade_search_fort_hook, render).apply() - || !INTERPOSE_HOOK(stocks_search_hook, feed).apply() || !INTERPOSE_HOOK(stocks_search_hook, render).apply()) + if (!gps || + !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || + !INTERPOSE_HOOK(unitlist_search_hook, render).apply() || + !INTERPOSE_HOOK(trade_search_merc_hook, feed).apply() || + !INTERPOSE_HOOK(trade_search_merc_hook, render).apply() || + !INTERPOSE_HOOK(trade_search_fort_hook, feed).apply() || + !INTERPOSE_HOOK(trade_search_fort_hook, render).apply() || + !INTERPOSE_HOOK(stocks_search_hook, feed).apply() || + !INTERPOSE_HOOK(stocks_search_hook, render).apply()) out.printerr("Could not insert Search hooks!\n"); return CR_OK; @@ -613,9 +618,17 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event ) { - unitlist_search_hook::module.reset_all(); - trade_search_merc_hook::module.reset_all(); - trade_search_fort_hook::module.reset_all(); - stocks_search_hook::module.reset_all(); + switch (event) { + case SC_VIEWSCREEN_CHANGED: + unitlist_search_hook::module.reset_all(); + trade_search_merc_hook::module.reset_all(); + trade_search_fort_hook::module.reset_all(); + stocks_search_hook::module.reset_all(); + break; + + default: + break; + } + return CR_OK; -} \ No newline at end of file +} From 9bf24bde10154e335dfe96dfb1cb202dd9027cc1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 3 Nov 2012 15:34:04 +0400 Subject: [PATCH 45/79] More various updates for search. - Keep the search state as long as the screen is alive. - Properly forget saved state when clearing search. - Fix the start column in render for stocks screen. - Allow search by profession in all Units pages. - Dismiss search when trying to trade. --- plugins/search.cpp | 102 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/plugins/search.cpp b/plugins/search.cpp index e19c2e3c0..cc3f29c12 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -10,6 +10,7 @@ #include "df/viewscreen_tradegoodsst.h" #include "df/viewscreen_unitlistst.h" #include "df/interface_key.h" +#include "df/interfacest.h" using std::set; using std::vector; @@ -19,6 +20,7 @@ using namespace DFHack; using namespace df::enums; using df::global::gps; +using df::global::gview; /* Search Plugin @@ -39,6 +41,14 @@ void OutputString(int8_t color, int &x, int y, const std::string &text) x += text.length(); } +static bool is_live_screen(const df::viewscreen *screen) +{ + for (df::viewscreen *cur = &gview->view; cur; cur = cur->child) + if (cur == screen) + return true; + return false; +} + // // START: Base Search functionality // @@ -60,6 +70,15 @@ public: track_secondary_values = false; } + bool reset_on_change() + { + if (valid && is_live_screen(viewscreen)) + return false; + + reset_all(); + return true; + } + // A new keystroke is received in a searchable screen virtual bool process_input(set *input) { @@ -245,6 +264,9 @@ protected: update_secondary_values(); *sort_list2 = saved_list2; } + + saved_list1.clear(); + saved_list2.clear(); } store_reference_values(); search_string = ""; @@ -346,7 +368,12 @@ struct search_hook : T DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) { - module.init(this); + if (!module.init(this)) + { + INTERPOSE_NEXT(feed)(input); + return; + } + if (!module.process_input(input)) { INTERPOSE_NEXT(feed)(input); @@ -357,9 +384,10 @@ struct search_hook : T DEFINE_VMETHOD_INTERPOSE(void, render, ()) { - module.init(this); + bool ok = module.init(this); INTERPOSE_NEXT(render)(); - module.render(); + if (ok) + module.render(); } }; @@ -382,10 +410,10 @@ public: virtual void render() const { if (!viewscreen->in_group_mode) - print_search_option(1); + print_search_option(2); else { - int x = 1; + int x = 2; OutputString(15, x, gps->dimy - 2, "Tab to enable Search"); } } @@ -402,13 +430,18 @@ public: search_parent::do_post_update_check(); } - virtual void init(df::viewscreen_storesst *screen) + bool init(df::viewscreen_storesst *screen) { + if (screen != viewscreen && !reset_on_change()) + return false; + if (!valid) { viewscreen = screen; search_parent::init(&screen->item_cursor, &screen->items); } + + return true; } @@ -461,21 +494,25 @@ public: print_search_option(28); } - virtual void init(df::viewscreen_unitlistst *screen) + bool init(df::viewscreen_unitlistst *screen) { + if (screen != viewscreen && !reset_on_change()) + return false; + if (!valid) { viewscreen = screen; search_parent::init(&screen->cursor_pos[viewscreen->page], &screen->units[viewscreen->page], &screen->jobs[viewscreen->page]); } + + return true; } private: virtual string get_element_description(df::unit *element) const { string desc = Translation::TranslateName(Units::getVisibleName(element), false); - if (viewscreen->page == 1) - desc += Units::getProfessionName(element); // Check animal type too + desc += ", " + Units::getProfessionName(element); // Check animal type too return desc; } @@ -529,6 +566,29 @@ private: { return Items::getDescription(element, 0, true); } + + virtual bool should_check_input(set *input) + { + if (is_entry_mode()) + return true; + + if (input->count(interface_key::TRADE_TRADE) || + input->count(interface_key::TRADE_OFFER) || + input->count(interface_key::TRADE_SEIZE)) + { + // Block the keys if were searching + if (!search_string.empty()) + input->clear(); + + // Trying to trade, reset search + clear_search(); + reset_all(); + + return false; + } + + return true; + } }; @@ -540,14 +600,19 @@ public: print_search_option(2, 26); } - virtual void init(df::viewscreen_tradegoodsst *screen) + bool init(df::viewscreen_tradegoodsst *screen) { + if (screen != viewscreen && !reset_on_change()) + return false; + if (!valid) { viewscreen = screen; search_parent::init(&screen->trader_cursor, &screen->trader_items, &screen->trader_selected, 'q'); track_secondary_values = true; } + + return true; } }; @@ -564,14 +629,19 @@ public: print_search_option(42, 26); } - virtual void init(df::viewscreen_tradegoodsst *screen) + bool init(df::viewscreen_tradegoodsst *screen) { + if (screen != viewscreen && !reset_on_change()) + return false; + if (!valid) { viewscreen = screen; search_parent::init(&screen->broker_cursor, &screen->broker_items, &screen->broker_selected, 'w'); track_secondary_values = true; } + + return true; } }; @@ -589,7 +659,7 @@ DFHACK_PLUGIN("search"); DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { - if (!gps || + if (!gps || !gview || !INTERPOSE_HOOK(unitlist_search_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_search_hook, render).apply() || !INTERPOSE_HOOK(trade_search_merc_hook, feed).apply() || @@ -620,10 +690,10 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch { switch (event) { case SC_VIEWSCREEN_CHANGED: - unitlist_search_hook::module.reset_all(); - trade_search_merc_hook::module.reset_all(); - trade_search_fort_hook::module.reset_all(); - stocks_search_hook::module.reset_all(); + unitlist_search_hook::module.reset_on_change(); + trade_search_merc_hook::module.reset_on_change(); + trade_search_fort_hook::module.reset_on_change(); + stocks_search_hook::module.reset_on_change(); break; default: From d6f1bb93b5397338d9c6761ed9ce4d3a6d01e999 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 3 Nov 2012 15:49:34 +0400 Subject: [PATCH 46/79] Add documentation for the search plugin. --- Readme.html | 104 +++++++++++++++++++++++++++++++--------------------- Readme.rst | 25 +++++++++++++ 2 files changed, 87 insertions(+), 42 deletions(-) diff --git a/Readme.html b/Readme.html index 0da481c88..cd073459c 100644 --- a/Readme.html +++ b/Readme.html @@ -500,33 +500,34 @@ access DF memory and allow for easier development of new tools.

  • In-game interface tools
  • -
  • Behavior Mods
      -
    • Siege Engine
        -
      • Rationale
      • -
      • Configuration UI
      • +
      • Behavior Mods @@ -2691,14 +2692,33 @@ cursor onto that cell instead of toggling it.
      • Pressing ESC normally returns to the unit screen, but Shift-ESC would exit directly to the main dwarf mode screen.

        +
        -

        gui/liquids

        +

        gui/liquids

        To use, bind to a key and activate in the 'k' mode.

        While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

        -

        gui/mechanisms

        +

        gui/mechanisms

        To use, bind to a key and activate in the 'q' mode.

        Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

        @@ -2707,7 +2727,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

        -

        gui/rename

        +

        gui/rename

        Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

          @@ -2723,14 +2743,14 @@ It is also possible to rename zones from the 'i' menu.

          The building or unit options are automatically assumed when in relevant ui state.

        -

        gui/room-list

        +

        gui/room-list

        To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

        The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

        -

        gui/choose-weapons

        +

        gui/choose-weapons

        Bind to a key, and activate in the Equip->View/Customize page of the military screen.

        Depending on the cursor location, it rewrites all 'individual choice weapon' entries in the selected squad or position to use a specific weapon type matching the assigned @@ -2740,13 +2760,13 @@ only that entry, and does it even if it is not 'individual choice'.

        and may lead to inappropriate weapons being selected.

        -

        gui/guide-path

        +

        gui/guide-path

        Bind to a key, and activate in the Hauling menu with the cursor over a Guide order.

        The script displays the cached path that will be used by the order; the game computes it when the order is executed for the first time.

        -

        gui/workshop-job

        +

        gui/workshop-job

        Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

        The script shows a list of the input reagents of the selected job, and allows changing them like the job item-type and job item-material commands.

        @@ -2774,7 +2794,7 @@ and then try to change the input item type, now it won't let you select plan you have to unset the material first.

        -

        gui/workflow

        +

        gui/workflow

        Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

        This script provides a simple interface to constraints managed by the workflow plugin. When active, it displays a list of all constraints applicable to the @@ -2796,7 +2816,7 @@ as described in workflow documentation above. can be used for troubleshooting jobs that don't match the right constraints.

        -

        gui/assign-rack

        +

        gui/assign-rack

        Bind to a key, and activate when viewing a weapon rack in the 'q' mode.

        This script is part of a group of related fixes to make the armory storage work again. The existing issues are:

        @@ -2816,7 +2836,7 @@ the intended user.

        -

        Behavior Mods

        +

        Behavior Mods

        These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

        @@ -2827,20 +2847,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

        -

        Siege Engine

        +

        Siege Engine

        The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

        -

        Rationale

        +

        Rationale

        Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

        -

        Configuration UI

        +

        Configuration UI

        The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

        The main mode displays the current target, selected ammo item type, linked stockpiles and @@ -2861,7 +2881,7 @@ menu.

        -

        Power Meter

        +

        Power Meter

        The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

        The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -2870,11 +2890,11 @@ key and activate after selecting Pressure Plate in the build menu.

        configuration page, but configures parameters relevant to the modded power meter building.

        -

        Steam Engine

        +

        Steam Engine

        The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

        -

        Rationale

        +

        Rationale

        The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2885,7 +2905,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

        -

        Construction

        +

        Construction

        The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

        @@ -2909,7 +2929,7 @@ short axles that can be built later than both of the engines.

        -

        Operation

        +

        Operation

        In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -2940,7 +2960,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

        -

        Explosions

        +

        Explosions

        The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

        During operation weak parts get gradually worn out, and @@ -2949,7 +2969,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

        -

        Save files

        +

        Save files

        It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -2960,7 +2980,7 @@ being generated.

        -

        Add Spatter

        +

        Add Spatter

        This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/Readme.rst b/Readme.rst index 0b2fbc378..d9021c7cb 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1898,6 +1898,31 @@ Pressing ESC normally returns to the unit screen, but Shift-ESC would exit directly to the main dwarf mode screen. +Search +====== + +The search plugin adds search to the Stocks, Trading and Unit List screens. + +Searching works the same way as the search option in "Move to Depot" does. +You will see the Search option displayed on screen with a hotkey (usually 's'). +Pressing it lets you start typing a query and the relevant list will start +filtering automatically. + +Pressing ENTER, ESC or the arrow keys will return you to browsing the now +filtered list, which still functions as normal. You can clear the filter +by either going back into search mode and backspacing to delete it, or +pressing the "shifted" version of the search hotkey while browsing the +list (e.g. if the hotkey is 's', then hitting 'shift-s' will clear any +filter). + +Leaving any screen automatically clears the filter. + +In the Trade screen, the actual trade will always only act on items that +are actually visible in the list; the same effect applies to the Trade +Value numbers displayed by the screen. Because of this, pressing the 't' +key while search is active clears the search instead of executing the trade. + + gui/liquids =========== From 959831685506a3c92f8af0f6678afa91c95c4aa8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 3 Nov 2012 20:06:33 +0400 Subject: [PATCH 47/79] Add a native pen object for lua with a more checked behavior. --- Lua API.html | 82 +++++--- Lua API.rst | 76 +++++-- library/LuaApi.cpp | 349 ++++++++++++++++++++++++++++--- library/include/LuaTools.h | 6 + library/include/modules/Screen.h | 2 + library/lua/gui.lua | 62 +++--- library/lua/gui/dialogs.lua | 4 +- 7 files changed, 466 insertions(+), 115 deletions(-) diff --git a/Lua API.html b/Lua API.html index a52347104..5dff4b3e4 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1555,37 +1555,12 @@ be feasibly used in the core context.

        Checks if [GRAPHICS:YES] was specified in init.

      • dfhack.screen.paintTile(pen,x,y[,char,tile])

        -

        Paints a tile using given parameters. Pen is a table with following possible fields:

        -
        -
        ch
        -

        Provides the ordinary tile character, as either a 1-character string or a number. -Can be overridden with the char function parameter.

        -
        -
        fg
        -

        Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).

        -
        -
        bg
        -

        Background color for the ordinary tile. Defaults to COLOR_BLACK (0).

        -
        -
        bold
        -

        Bright/bold text flag. If nil, computed based on (fg & 8); fg is masked to 3 bits. -Otherwise should be true/false.

        -
        -
        tile
        -

        Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.

        -
        -
        tile_color = true
        -

        Specifies that the tile should be shaded with fg/bg.

        -
        -
        tile_fg, tile_bg
        -

        If specified, overrides tile_color and supplies shading colors directly.

        -
        -
        +

        Paints a tile using given parameters. See below for a description of pen.

        Returns false if coordinates out of bounds, or other error.

      • dfhack.screen.readTile(x,y)

        Retrieves the contents of the specified tile from the screen buffers. -Returns a pen, or nil if invalid or TrueType.

        +Returns a pen object, or nil if invalid or TrueType.

      • dfhack.screen.paintString(pen,x,y,text)

        Paints the string starting at x,y. Uses the string characters @@ -1610,6 +1585,59 @@ The values can then be used for the tile field of pen structur functions in this section, this may be used at any time.

      +

      The "pen" argument used by functions above may be represented by +a table with the following possible fields:

      +
      +
      +
      ch
      +
      Provides the ordinary tile character, as either a 1-character string or a number. +Can be overridden with the char function parameter.
      +
      fg
      +
      Foreground color for the ordinary tile. Defaults to COLOR_GREY (7).
      +
      bg
      +
      Background color for the ordinary tile. Defaults to COLOR_BLACK (0).
      +
      bold
      +
      Bright/bold text flag. If nil, computed based on (fg & 8); fg is masked to 3 bits. +Otherwise should be true/false.
      +
      tile
      +
      Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt.
      +
      tile_color = true
      +
      Specifies that the tile should be shaded with fg/bg.
      +
      tile_fg, tile_bg
      +
      If specified, overrides tile_color and supplies shading colors directly.
      +
      +
      +

      Alternatively, it may be a pre-parsed native object with the following API:

      +
        +
      • dfhack.pen.make(base[,pen_or_fg,bg,bold])

        +

        Creates a new pre-parsed pen by combining its arguments according to the +following rules:

        +
          +
        1. The base argument may be a pen object, a pen table as specified above, +or a single color value. In the single value case, it is split into +fg and bold properties, and others are initialized to 0. +This argument will be converted to a pre-parsed object and returned +if there are no other arguments.
        2. +
        3. If the pen_or_fg argument is specified as a table or object, it +completely replaces the base, and is returned instead of it.
        4. +
        5. Otherwise, the non-nil subset of the optional arguments is used +to update the fg, bg and bold properties of the base. +If the bold flag is nil, but pen_or_fg is a number, bold +is deduced from it like in the simple base case.
        6. +
        +

        This function always returns a new pre-parsed pen, or nil.

        +
      • +
      • dfhack.pen.parse(base[,pen_or_fg,bg,bold])

        +

        Exactly like the above function, but returns base or pen_or_fg +directly if they are already a pre-parsed native object.

        +
      • +
      • pen.property, pen.property = value, pairs(pen)

        +

        Pre-parsed pens support reading and setting their properties, +but don't behave exactly like a simple table would; for instance, +assigning to pen.tile_color also resets pen.tile_fg and +pen.tile_bg to nil.

        +
      • +

      In order to actually be able to paint to the screen, it is necessary to create and register a viewscreen (basically a modal dialog) with the game.

      diff --git a/Lua API.rst b/Lua API.rst index bd712d301..ec48938db 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1403,31 +1403,14 @@ Basic painting functions: * ``dfhack.screen.paintTile(pen,x,y[,char,tile])`` - Paints a tile using given parameters. Pen is a table with following possible fields: - - ``ch`` - Provides the ordinary tile character, as either a 1-character string or a number. - Can be overridden with the ``char`` function parameter. - ``fg`` - Foreground color for the ordinary tile. Defaults to COLOR_GREY (7). - ``bg`` - Background color for the ordinary tile. Defaults to COLOR_BLACK (0). - ``bold`` - Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits. - Otherwise should be *true/false*. - ``tile`` - Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt. - ``tile_color = true`` - Specifies that the tile should be shaded with *fg/bg*. - ``tile_fg, tile_bg`` - If specified, overrides *tile_color* and supplies shading colors directly. + Paints a tile using given parameters. See below for a description of pen. Returns *false* if coordinates out of bounds, or other error. * ``dfhack.screen.readTile(x,y)`` Retrieves the contents of the specified tile from the screen buffers. - Returns a pen, or *nil* if invalid or TrueType. + Returns a pen object, or *nil* if invalid or TrueType. * ``dfhack.screen.paintString(pen,x,y,text)`` @@ -1458,6 +1441,61 @@ Basic painting functions: Requests repaint of the screen by setting a flag. Unlike other functions in this section, this may be used at any time. +The "pen" argument used by functions above may be represented by +a table with the following possible fields: + + ``ch`` + Provides the ordinary tile character, as either a 1-character string or a number. + Can be overridden with the ``char`` function parameter. + ``fg`` + Foreground color for the ordinary tile. Defaults to COLOR_GREY (7). + ``bg`` + Background color for the ordinary tile. Defaults to COLOR_BLACK (0). + ``bold`` + Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is masked to 3 bits. + Otherwise should be *true/false*. + ``tile`` + Graphical tile id. Ignored unless [GRAPHICS:YES] was in init.txt. + ``tile_color = true`` + Specifies that the tile should be shaded with *fg/bg*. + ``tile_fg, tile_bg`` + If specified, overrides *tile_color* and supplies shading colors directly. + +Alternatively, it may be a pre-parsed native object with the following API: + +* ``dfhack.pen.make(base[,pen_or_fg,bg,bold])`` + + Creates a new pre-parsed pen by combining its arguments according to the + following rules: + + 1. The ``base`` argument may be a pen object, a pen table as specified above, + or a single color value. In the single value case, it is split into + ``fg`` and ``bold`` properties, and others are initialized to 0. + This argument will be converted to a pre-parsed object and returned + if there are no other arguments. + + 2. If the ``pen_or_fg`` argument is specified as a table or object, it + completely replaces the base, and is returned instead of it. + + 3. Otherwise, the non-nil subset of the optional arguments is used + to update the ``fg``, ``bg`` and ``bold`` properties of the base. + If the ``bold`` flag is *nil*, but *pen_or_fg* is a number, ``bold`` + is deduced from it like in the simple base case. + + This function always returns a new pre-parsed pen, or *nil*. + +* ``dfhack.pen.parse(base[,pen_or_fg,bg,bold])`` + + Exactly like the above function, but returns ``base`` or ``pen_or_fg`` + directly if they are already a pre-parsed native object. + +* ``pen.property``, ``pen.property = value``, ``pairs(pen)`` + + Pre-parsed pens support reading and setting their properties, + but don't behave exactly like a simple table would; for instance, + assigning to ``pen.tile_color`` also resets ``pen.tile_fg`` and + ``pen.tile_bg`` to *nil*. + In order to actually be able to paint to the screen, it is necessary to create and register a viewscreen (basically a modal dialog) with the game. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index e7424ad50..0151ed404 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -725,6 +725,316 @@ static void OpenMatinfo(lua_State *state) lua_pop(state, 1); } +/************** + * Pen object * + **************/ + +static int DFHACK_PEN_TOKEN = 0; + +void Lua::Push(lua_State *L, const Screen::Pen &info) +{ + if (!info.valid()) + { + lua_pushnil(L); + return; + } + + void *pdata = lua_newuserdata(L, sizeof(Pen)); + + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN); + lua_setmetatable(L, -2); + + new (pdata) Pen(info); +} + +static Pen *check_pen_native(lua_State *L, int index) +{ + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN); + + if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2)) + luaL_argerror(L, index, "not a pen object"); + + lua_pop(L, 2); + + return (Pen*)lua_touserdata(L, index); +} + +void Lua::CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil, bool allow_color) +{ + index = lua_absindex(L, index); + + luaL_checkany(L, index); + + if (lua_isnil(L, index)) + { + if (!allow_nil) + luaL_argerror(L, index, "nil pen not allowed"); + + *pen = Pen(0,0,0,-1); + } + else if (lua_isuserdata(L, index)) + { + *pen = *check_pen_native(L, index); + } + else if (allow_color && lua_isnumber(L, index)) + { + *pen = Pen(0, lua_tointeger(L, index)&15, 0); + } + else + { + luaL_checktype(L, index, LUA_TTABLE); + decode_pen(L, *pen, index); + } +} + +static int adjust_pen(lua_State *L, bool no_copy) +{ + lua_settop(L, 4); + + Pen pen; + int iidx = 1; + Lua::CheckPen(L, &pen, 1, true, true); + + if (!lua_isnil(L, 2) || !lua_isnil(L, 3) || !lua_isnil(L, 4)) + { + if (lua_isnumber(L, 2) || lua_isnil(L, 2)) + { + if (!pen.valid()) + pen = Pen(); + + iidx = -1; + + pen.fg = luaL_optint(L, 2, pen.fg) & 15; + pen.bg = luaL_optint(L, 3, pen.bg); + + if (!lua_isnil(L, 4)) + pen.bold = lua_toboolean(L, 4); + else if (!lua_isnil(L, 2)) + { + pen.bold = !!(pen.fg & 8); + pen.fg &= 7; + } + } + else + { + iidx = 2; + Lua::CheckPen(L, &pen, 2, false, false); + } + } + + if (no_copy && iidx > 0 && lua_isuserdata(L, iidx)) + lua_pushvalue(L, iidx); + else + Lua::Push(L, pen); + + return 1; +} + +static int dfhack_pen_parse(lua_State *L) +{ + return adjust_pen(L, true); +} + +static int dfhack_pen_make(lua_State *L) +{ + return adjust_pen(L, false); +} + +static void make_pen_table(lua_State *L, Pen &pen) +{ + if (!pen.valid()) + luaL_error(L, "invalid pen state"); + else + { + lua_newtable(L); + lua_pushinteger(L, (unsigned char)pen.ch); lua_setfield(L, -2, "ch"); + lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg"); + lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg"); + lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold"); + + if (pen.tile) + { + lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile"); + } + + switch (pen.tile_mode) { + case Pen::CharColor: + lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color"); + break; + case Pen::TileColor: + lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg"); + lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg"); + break; + default: + lua_pushboolean(L, false); lua_setfield(L, -2, "tile_color"); + break; + } + } +} + +static void get_pen_mirror(lua_State *L, int idx) +{ + lua_getuservalue(L, idx); + + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + + Pen pen; + Lua::CheckPen(L, &pen, idx, false, false); + make_pen_table(L, pen); + + lua_dup(L); + lua_setuservalue(L, idx); + } +} + +static int dfhack_pen_index(lua_State *L) +{ + lua_settop(L, 2); + luaL_checktype(L, 1, LUA_TUSERDATA); + + // check metatable + if (!lua_getmetatable(L, 1)) + luaL_argerror(L, 1, "must be a pen"); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if (!lua_isnil(L, -1)) + return 1; + + // otherwise read from the mirror table, creating it if necessary + lua_settop(L, 2); + get_pen_mirror(L, 1); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + return 1; +} + +static int pen_pnext(lua_State *L) +{ + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, lua_upvalueindex(1))) + return 2; + lua_pushnil(L); + return 1; +} + +static int dfhack_pen_pairs(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TUSERDATA); + get_pen_mirror(L, 1); + lua_pushcclosure(L, pen_pnext, 1); + lua_pushnil(L); + lua_pushnil(L); + return 3; +} + +const char *const pen_fields[] = { + "ch", "fg", "bold", "bg", "tile", "tile_color", "tile_fg", "tile_bg", NULL +}; + +static int dfhack_pen_newindex(lua_State *L) +{ + lua_settop(L, 3); + luaL_checktype(L, 1, LUA_TUSERDATA); + int id = luaL_checkoption(L, 2, NULL, pen_fields); + int arg = 0; + Pen &pen = *check_pen_native(L, 1); + bool wipe_tile = false, wipe_tc = false; + + switch (id) { + case 0: + if (lua_type(L, 3) != LUA_TNUMBER) + arg = (unsigned char)*luaL_checkstring(L, 3); + else + arg = luaL_checkint(L, 3); + pen.ch = arg; + lua_pushinteger(L, (unsigned char)pen.ch); + break; + case 1: + pen.fg = luaL_checkint(L, 3) & 15; + lua_pushinteger(L, pen.fg); + break; + case 2: + pen.bold = lua_toboolean(L, 3); + lua_pushboolean(L, pen.bold); + break; + case 3: + pen.bg = luaL_checkint(L, 3) & 15; + lua_pushinteger(L, pen.bg); + break; + case 4: + arg = lua_isnil(L, 3) ? 0 : luaL_checkint(L, 3); + if (arg < 0) + luaL_argerror(L, 3, "invalid tile index"); + pen.tile = arg; + if (pen.tile) + lua_pushinteger(L, pen.tile); + else + lua_pushnil(L); + break; + case 5: + wipe_tile = (pen.tile_mode == Pen::TileColor); + pen.tile_mode = lua_toboolean(L, 3) ? Pen::CharColor : Pen::AsIs; + lua_pushboolean(L, pen.tile_mode == Pen::CharColor); + break; + case 6: + if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_bg = 0; } + pen.tile_fg = luaL_checkint(L, 3) & 15; + pen.tile_mode = Pen::TileColor; + lua_pushinteger(L, pen.tile_fg); + break; + case 7: + if (pen.tile_mode != Pen::TileColor) { wipe_tc = true; pen.tile_fg = 7; } + pen.tile_bg = luaL_checkint(L, 3) & 15; + pen.tile_mode = Pen::TileColor; + lua_pushinteger(L, pen.tile_bg); + break; + } + + lua_getuservalue(L, 1); + + if (!lua_isnil(L, -1)) + { + lua_remove(L, 3); + lua_insert(L, 2); + lua_rawset(L, 2); + + if (wipe_tc) { + lua_pushnil(L); lua_setfield(L, 2, "tile_color"); + lua_pushinteger(L, pen.tile_fg); lua_setfield(L, 2, "tile_fg"); + lua_pushinteger(L, pen.tile_bg); lua_setfield(L, 2, "tile_bg"); + } + if (wipe_tile) { + lua_pushnil(L); lua_setfield(L, 2, "tile_fg"); + lua_pushnil(L); lua_setfield(L, 2, "tile_bg"); + } + } + + return 0; +} + +static const luaL_Reg dfhack_pen_funcs[] = { + { "parse", dfhack_pen_parse }, + { "make", dfhack_pen_make }, + { "__index", dfhack_pen_index }, + { "__pairs", dfhack_pen_pairs }, + { "__newindex", dfhack_pen_newindex }, + { NULL, NULL } +}; + +static void OpenPen(lua_State *state) +{ + luaL_getsubtable(state, lua_gettop(state), "pen"); + + lua_dup(state); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN); + + luaL_setfuncs(state, dfhack_pen_funcs, 0); + + lua_pop(state, 1); +} + /************************ * Wrappers for C++ API * ************************/ @@ -1251,7 +1561,7 @@ static int screen_getWindowSize(lua_State *L) static int screen_paintTile(lua_State *L) { Pen pen; - decode_pen(L, pen, 1); + Lua::CheckPen(L, &pen, 1); int x = luaL_checkint(L, 2); int y = luaL_checkint(L, 3); if (lua_gettop(L) >= 4 && !lua_isnil(L, 4)) @@ -1272,44 +1582,14 @@ static int screen_readTile(lua_State *L) int x = luaL_checkint(L, 1); int y = luaL_checkint(L, 2); Pen pen = Screen::readTile(x, y); - - if (!pen.valid()) - { - lua_pushnil(L); - } - else - { - lua_newtable(L); - lua_pushinteger(L, pen.ch); lua_setfield(L, -2, "ch"); - lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg"); - lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg"); - lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold"); - - if (pen.tile) - { - lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile"); - - switch (pen.tile_mode) { - case Pen::CharColor: - lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color"); - break; - case Pen::TileColor: - lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg"); - lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg"); - break; - default: - break; - } - } - } - + Lua::Push(L, pen); return 1; } static int screen_paintString(lua_State *L) { Pen pen; - decode_pen(L, pen, 1); + Lua::CheckPen(L, &pen, 1); int x = luaL_checkint(L, 2); int y = luaL_checkint(L, 3); const char *text = luaL_checkstring(L, 4); @@ -1320,7 +1600,7 @@ static int screen_paintString(lua_State *L) static int screen_fillRect(lua_State *L) { Pen pen; - decode_pen(L, pen, 1); + Lua::CheckPen(L, &pen, 1); int x1 = luaL_checkint(L, 2); int y1 = luaL_checkint(L, 3); int x2 = luaL_checkint(L, 4); @@ -1720,6 +2000,7 @@ void OpenDFHackApi(lua_State *state) { OpenPersistent(state); OpenMatinfo(state); + OpenPen(state); LuaWrapper::SetFunctionWrappers(state, dfhack_module); OpenModule(state, "gui", dfhack_gui_module); diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index ec4917972..655d069d3 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -41,6 +41,9 @@ namespace DFHack { namespace Units { struct NoblePosition; } + namespace Screen { + struct Pen; + }; } namespace DFHack {namespace Lua { @@ -285,6 +288,7 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj); void Push(lua_State *state, const Units::NoblePosition &pos); DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info); + DFHACK_EXPORT void Push(lua_State *state, const Screen::Pen &info); template inline void Push(lua_State *state, T *ptr) { PushDFObject(state, ptr); } @@ -315,6 +319,8 @@ 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 void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true); + DFHACK_EXPORT bool IsCoreContext(lua_State *state); namespace Event { diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index ccd7f2f8d..d8b5774e9 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -76,6 +76,8 @@ namespace DFHack bool valid() const { return tile >= 0; } bool empty() const { return ch == 0 && tile == 0; } + // NOTE: LuaApi.cpp assumes this struct is plain data and has empty destructor + Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 15d03742f..cfb058f9d 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -6,7 +6,9 @@ local dscreen = dfhack.screen USE_GRAPHICS = dscreen.inGraphicsMode() -CLEAR_PEN = {ch=32,fg=0,bg=0} +local to_pen = dfhack.pen.parse + +CLEAR_PEN = to_pen{ch=32,fg=0,bg=0} function simulateInput(screen,...) local keys = {} @@ -116,16 +118,6 @@ function blink_visible(delay) return math.floor(dfhack.getTickCount()/delay) % 2 == 0 end -function to_pen(default, pen, bg, bold) - if pen == nil then - return default or {} - elseif type(pen) ~= 'table' then - return {fg=pen,bg=bg,bold=bold} - else - return pen - end -end - function getKeyDisplay(code) if type(code) == 'string' then code = df.interface_key[code] @@ -215,7 +207,8 @@ Painter = defclass(Painter, ViewRect) function Painter:init(args) self.x = self.x1 self.y = self.y1 - self.cur_pen = to_pen(nil, args.pen or COLOR_GREY) + self.cur_pen = to_pen(args.pen or COLOR_GREY) + self.cur_key_pen = to_pen(args.key_pen or COLOR_LIGHTGREEN) end function Painter.new(rect, pen) @@ -241,6 +234,7 @@ end function Painter:viewport(x,y,w,h) local vp = ViewRect.viewport(x,y,w,h) vp.cur_pen = self.cur_pen + vp.cur_key_pen = self.cur_key_pen return mkinstance(Painter, vp):seek(0,0) end @@ -280,10 +274,12 @@ function Painter:pen(pen,...) end function Painter:color(fg,bold,bg) - self.cur_pen = copyall(self.cur_pen) - self.cur_pen.fg = fg - self.cur_pen.bold = bold - if bg then self.cur_pen.bg = bg end + self.cur_pen = to_pen(self.cur_pen, fg, bg, bold) + return self +end + +function Painter:key_pen(pen,...) + self.cur_key_pen = to_pen(self.cur_key_pen, pen, ...) return self end @@ -339,10 +335,10 @@ function Painter:string(text,pen,...) return self:advance(#text, nil) end -function Painter:key(code,pen,bg,...) +function Painter:key(code,pen,...) return self:string( getKeyDisplay(code), - pen or COLOR_LIGHTGREEN, bg or self.cur_pen.bg, ... + to_pen(self.cur_key_pen, pen, ...) ) end @@ -557,28 +553,28 @@ end -- Plain grey-colored frame. GREY_FRAME = { - frame_pen = { ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY }, - title_pen = { fg = COLOR_BLACK, bg = COLOR_WHITE }, - signature_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, + frame_pen = to_pen{ ch = ' ', fg = COLOR_BLACK, bg = COLOR_GREY }, + title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_WHITE }, + signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY }, } -- The usual boundary used by the DF screens. Often has fancy pattern in tilesets. BOUNDARY_FRAME = { - frame_pen = { ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK }, - title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, - signature_pen = { fg = COLOR_BLACK, bg = COLOR_DARKGREY }, + frame_pen = to_pen{ ch = 0xDB, fg = COLOR_DARKGREY, bg = COLOR_BLACK }, + title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY }, + signature_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_DARKGREY }, } GREY_LINE_FRAME = { - frame_pen = { ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK }, - h_frame_pen = { ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK }, - v_frame_pen = { ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK }, - lt_frame_pen = { ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK }, - lb_frame_pen = { ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK }, - rt_frame_pen = { ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK }, - rb_frame_pen = { ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK }, - title_pen = { fg = COLOR_BLACK, bg = COLOR_GREY }, - signature_pen = { fg = COLOR_DARKGREY, bg = COLOR_BLACK }, + frame_pen = to_pen{ ch = 206, fg = COLOR_GREY, bg = COLOR_BLACK }, + h_frame_pen = to_pen{ ch = 205, fg = COLOR_GREY, bg = COLOR_BLACK }, + v_frame_pen = to_pen{ ch = 186, fg = COLOR_GREY, bg = COLOR_BLACK }, + lt_frame_pen = to_pen{ ch = 201, fg = COLOR_GREY, bg = COLOR_BLACK }, + lb_frame_pen = to_pen{ ch = 200, fg = COLOR_GREY, bg = COLOR_BLACK }, + rt_frame_pen = to_pen{ ch = 187, fg = COLOR_GREY, bg = COLOR_BLACK }, + rb_frame_pen = to_pen{ ch = 188, fg = COLOR_GREY, bg = COLOR_BLACK }, + title_pen = to_pen{ fg = COLOR_BLACK, bg = COLOR_GREY }, + signature_pen = to_pen{ fg = COLOR_DARKGREY, bg = COLOR_BLACK }, } function paint_frame(x1,y1,x2,y2,style,title) diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 5811e94e6..0a79b4c3e 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -160,8 +160,8 @@ function ListBox:preinit(info) end function ListBox:init(info) - local spen = gui.to_pen(COLOR_CYAN, self.select_pen, nil, false) - local cpen = gui.to_pen(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) + local spen = dfhack.pen.parse(COLOR_CYAN, self.select_pen, nil, false) + local cpen = dfhack.pen.parse(COLOR_LIGHTCYAN, self.cursor_pen or self.select_pen, nil, true) local list_widget = widgets.List if self.with_filter then From bd8c59462c897207a6f386a73d93bb0f7fe33182 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 4 Nov 2012 17:06:32 +0400 Subject: [PATCH 48/79] Add documentation for the core lua gui library stuff. --- Lua API.html | 833 +++++++++++++++++++++++++++++++++++- Lua API.rst | 782 ++++++++++++++++++++++++++++++++- library/lua/gui/widgets.lua | 5 +- library/lua/utils.lua | 1 + 4 files changed, 1603 insertions(+), 18 deletions(-) diff --git a/Lua API.html b/Lua API.html index 5dff4b3e4..226afa98a 100644 --- a/Lua API.html +++ b/Lua API.html @@ -377,12 +377,34 @@ ul.auto-toc {
    • class
  • -
  • Plugins

    The current version of DFHack has extensive support for @@ -1584,6 +1606,10 @@ The values can then be used for the tile field of pen structur

    Requests repaint of the screen by setting a flag. Unlike other functions in this section, this may be used at any time.

  • +
  • dfhack.screen.getKeyDisplay(key)

    +

    Returns the string that should be used to represent the given +logical keybinding on the screen in texts like "press Key to ...".

    +
  • The "pen" argument used by functions above may be represented by a table with the following possible fields:

    @@ -1889,6 +1915,19 @@ SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED

  • Functions already described above

    safecall, qerror, mkmodule, reload

  • +
  • Miscellaneous constants

    + +++ + + + + + + +
    NEWLINE, COMMA, PERIOD:
     evaluate to the relevant character strings.
    DEFAULT_NIL:is an unspecified unique token used by the class module below.
    +
  • printall(obj)

    If the argument is a lua table or DF object reference, prints all fields.

  • @@ -1988,11 +2027,25 @@ are converted to 1-based lua sequences.

    as a guide to which values should be skipped as uninteresting. The force argument makes it always return a non-nil value.

    +
  • utils.parse_bitfield_int(value, type_ref)

    +

    Given an int value, and a bitfield type in the df tree, +it returns a lua table mapping the enabled bit keys to true, +unless value is 0, in which case it returns nil.

    +
  • +
  • utils.list_bitfield_flags(bitfield[, list])

    +

    Adds all enabled bitfield keys to list or a newly-allocated +empty sequence, and returns it. The bitfield argument may +be nil.

    +
  • utils.sort_vector(vector,field,cmpfun)

    Sorts a native vector or lua sequence using the comparator function. If field is not nil, applies the comparator to the field instead of the whole object.

  • +
  • utils.linear_index(vector,key[,field])

    +

    Searches for key in the vector, and returns index, found_value, +or nil if none found.

    +
  • utils.binsearch(vector,key,field,cmpfun,min,max)

    Does a binary search in a native vector or lua sequence for key, using cmpfun and field like sort_vector. @@ -2021,6 +2074,19 @@ utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')

  • utils.erase_sorted(vector,item,field,cmpfun)

    Exactly like erase_sorted_key, but if field is specified, takes the key from item[field].

  • +
  • utils.call_with_string(obj,methodname,...)

    +

    Allocates a temporary string object, calls obj:method(tmp,...), and +returns the value written into the temporary after deleting it.

    +
  • +
  • utils.getBuildingName(building)

    +

    Returns the string description of the given building.

    +
  • +
  • utils.getBuildingCenter(building)

    +

    Returns an x/y/z table pointing at the building center.

    +
  • +
  • utils.split_string(string, delimiter)

    +

    Splits the string by the given delimiter, and returns a sequence of results.

    +
  • utils.prompt_yes_no(prompt, default)

    Presents a yes/no prompt to the user. If default is not nil, allows just pressing Enter to submit the default choice. @@ -2072,19 +2138,31 @@ calling superclass methods.

    from fields in the table used as the constructor argument. If omitted, they are initialized with the default values specified in this declaration.

    If the default value should be nil, use ATTRS { foo = DEFAULT_NIL }.

    +

    Declaring an attribute is mostly the same as defining your init method like this:

    +
    +function Class.init(args)
    +    self.attr1 = args.attr1 or default1
    +    self.attr2 = args.attr2 or default2
    +    ...
    +end
    +
    +

    The main difference is that attributes are processed as a separate +initialization step, before any init methods are called. They +also make the directy relation between instance fields and constructor +arguments more explicit.

  • new_obj = Class{ foo = arg, bar = arg, ... }

    Calling the class as a function creates and initializes a new instance. Initialization happens in this order:

    1. An empty instance table is created, and its metatable set.
    2. -
    3. The preinit method is called via invoke_before (see below) -with the table used as argument to the class. This method is intended +
    4. The preinit methods are called via invoke_before (see below) +with the table used as argument to the class. These methods are intended for validating and tweaking that argument table.
    5. Declared ATTRS are initialized from the argument table or their default values.
    6. -
    7. The init method is called via invoke_after with the argument table. +
    8. The init methods are called via invoke_after with the argument table. This is the main constructor method.
    9. -
    10. The postinit method is called via invoke_after with the argument table. +
    11. The postinit methods are called via invoke_after with the argument table. Place code that should be called after the object is fully constructed here.
  • @@ -2099,6 +2177,12 @@ Place code that should be called after the object is fully constructed here. +
  • instance:cb_getfield(field_name)

    +

    Returns a closure that returns the specified field of the object when called.

    +
  • +
  • instance:cb_setfield(field_name)

    +

    Returns a closure that sets the specified field to its argument when called.

    +
  • instance:invoke_before(method_name, args...)

    Navigates the inheritance chain of the instance starting from the most specific class, and invokes the specified method with the arguments if it is defined in @@ -2123,15 +2207,740 @@ library itself uses them for constructors.

    To avoid confusion, these methods cannot be redefined.

    +
    +

    In-game UI Library

    +

    A number of lua modules with names starting with gui are dedicated +to wrapping the natives of the dfhack.screen module in a way that +is easy to use. This allows relatively easily and naturally creating +dialogs that integrate in the main game UI window.

    +

    These modules make extensive use of the class module, and define +things ranging from the basic Painter, View and Screen +classes, to fully functional predefined dialogs.

    +
    +

    gui

    +

    This module defines the most important classes and functions for +implementing interfaces. This documents those of them that are +considered stable.

    +
    +

    Misc

    +
      +
    • USE_GRAPHICS

      +

      Contains the value of dfhack.screen.inGraphicsMode(), which cannot be +changed without restarting the game and thus is constant during the session.

      +
    • +
    • CLEAR_PEN

      +

      The black pen used to clear the screen.

      +
    • +
    • simulateInput(screen, keys...)

      +

      This function wraps an undocumented native function that passes a set of +keycodes to a screen, and is the official way to do that.

      +

      Every argument after the initial screen may be nil, a numeric keycode, +a string keycode, a sequence of numeric or string keycodes, or a mapping +of keycodes to true or false. For instance, it is possible to use the +table passed as argument to onInput.

      +
    • +
    • mkdims_xy(x1,y1,x2,y2)

      +

      Returns a table containing the arguments as fields, and also width and +height that contains the rectangle dimensions.

      +
    • +
    • mkdims_wh(x1,y1,width,height)

      +

      Returns the same kind of table as mkdims_xy, only this time it computes +x2 and y2.

      +
    • +
    • is_in_rect(rect,x,y)

      +

      Checks if the given point is within a rectangle, represented by a table produced +by one of the mkdims functions.

      +
    • +
    • blink_visible(delay)

      +

      Returns true or false, with the value switching to the opposite every delay +msec. This is intended for rendering blinking interface objects.

      +
    • +
    • getKeyDisplay(keycode)

      +

      Wraps dfhack.screen.getKeyDisplay in order to allow using strings for the keycode argument.

      +
    • +
    +
    +
    +

    ViewRect class

    +

    This class represents an on-screen rectangle with an associated independent +clip area rectangle. It is the base of the Painter class, and is used by +Views to track their client area.

    +
      +
    • ViewRect{ rect = ..., clip_rect = ..., view_rect = ..., clip_view = ... }

      +

      The constructor has the following arguments:

      + +++ + + + + + + + + + +
      rect:The mkdims rectangle in screen coordinates of the logical viewport. +Defaults to the whole screen.
      clip_rect:The clip rectangle in screen coordinates. Defaults to rect.
      view_rect:A ViewRect object to copy from; overrides both rect and clip_rect.
      clip_view:A ViewRect object to intersect the specified clip area with.
      +
    • +
    • rect:isDefunct()

      +

      Returns true if the clip area is empty, i.e. no painting is possible.

      +
    • +
    • rect:inClipGlobalXY(x,y)

      +

      Checks if these global coordinates are within the clip rectangle.

      +
    • +
    • rect:inClipLocalXY(x,y)

      +

      Checks if these coordinates (specified relative to x1,y1) are within the clip rectangle.

      +
    • +
    • rect:localXY(x,y)

      +

      Converts a pair of global coordinates to local; returns x_local,y_local.

      +
    • +
    • rect:globalXY(x,y)

      +

      Converts a pair of local coordinates to global; returns x_global,y_global.

      +
    • +
    • rect:viewport(x,y,w,h) or rect:viewport(subrect)

      +

      Returns a ViewRect representing a sub-rectangle of the current one. +The arguments are specified in local coordinates; the subrect +argument must be a mkdims table. The returned object consists of +the exact specified rectangle, and a clip area produced by intersecting +it with the clip area of the original object.

      +
    • +
    +
    +
    +

    Painter class

    +

    The painting natives in dfhack.screen apply to the whole screen, are +completely stateless and don't implement clipping.

    +

    The Painter class inherits from ViewRect to provide clipping and local +coordinates, and tracks current cursor position and current pen.

    +
      +
    • Painter{ ..., pen = ..., key_pen = ... }

      +

      In addition to ViewRect arguments, Painter accepts a suggestion of +the initial value for the main pen, and the keybinding pen. They +default to COLOR_GREY and COLOR_LIGHTGREEN otherwise.

      +

      There are also some convenience functions that wrap this constructor:

      +
        +
      • Painter.new(rect,pen)
      • +
      • Painter.new_view(view_rect,pen)
      • +
      • Painter.new_xy(x1,y1,x2,y2,pen)
      • +
      • Painter.new_wh(x1,y1,width,height,pen)
      • +
      +
    • +
    • painter:isValidPos()

      +

      Checks if the current cursor position is within the clip area.

      +
    • +
    • painter:viewport(x,y,w,h)

      +

      Like the superclass method, but returns a Painter object.

      +
    • +
    • painter:cursor()

      +

      Returns the current cursor x,y in local coordinates.

      +
    • +
    • painter:seek(x,y)

      +

      Sets the current cursor position, and returns self. +Either of the arguments may be nil to keep the current value.

      +
    • +
    • painter:advance(dx,dy)

      +

      Adds the given offsets to the cursor position, and returns self. +Either of the arguments may be nil to keep the current value.

      +
    • +
    • painter:newline([dx])

      +

      Advances the cursor to the start of the next line plus the given x offset, and returns self.

      +
    • +
    • painter:pen(...)

      +

      Sets the current pen to dfhack.pen.parse(old_pen,...), and returns self.

      +
    • +
    • painter:key_pen(...)

      +

      Sets the current keybinding pen to dfhack.pen.parse(old_pen,...), and returns self.

      +
    • +
    • painter:clear()

      +

      Fills the whole clip rectangle with CLEAR_PEN, and returns self.

      +
    • +
    • painter:fill(x1,y1,x2,y2[,...]) or painter:fill(rect[,...])

      +

      Fills the specified local coordinate rectangle with dfhack.pen.parse(cur_pen,...), +and returns self.

      +
    • +
    • painter:char([char[, ...]])

      +

      Paints one character using char and dfhack.pen.parse(cur_pen,...); returns self. +The char argument, if not nil, is used to override the ch property of the pen.

      +
    • +
    • painter:tile([char, tile[, ...]])

      +

      Like above, but also allows overriding the tile property on ad-hoc basis.

      +
    • +
    • painter:string(text[, ...])

      +

      Paints the string with dfhack.pen.parse(cur_pen,...); returns self.

      +
    • +
    • painter:key(keycode[, ...])

      +

      Paints the description of the keycode using dfhack.pen.parse(cur_key_pen,...); returns self.

      +
    • +
    +

    As noted above, all painting methods return self, in order to allow chaining them like this:

    +
    +painter:pen(foo):seek(x,y):char(1):advance(1):string('bar')...
    +
    +
    +
    +

    View class

    +

    This class is the common abstract base of both the stand-alone screens +and common widgets to be used inside them. It defines the basic layout, +rendering and event handling framework.

    +

    The class defines the following attributes:

    + +++ + + + + + + + +
    visible:Specifies that the view should be painted.
    active:Specifies that the view should receive events, if also visible.
    view_id:Specifies an identifier to easily identify the view among subviews. +This is reserved for implementation of top-level views, and should +not be used by widgets for their internal subviews.
    +

    It also always has the following fields:

    + +++ + + + +
    subviews:Contains a table of all subviews. The sequence part of the +table is used for iteration. In addition, subviews are also +indexed under their view_id, if any; see addviews() below.
    +

    These fields are computed by the layout process:

    + +++ + + + + + + + + +
    frame_parent_rect:
     The ViewRect represeting the client area of the parent view.
    frame_rect:The mkdims rect of the outer frame in parent-local coordinates.
    frame_body:The ViewRect representing the body part of the View's own frame.
    +

    The class has the following methods:

    +
      +
    • view:addviews(list)

      +

      Adds the views in the list to the subviews sequence. If any of the views +in the list have view_id attributes that don't conflict with existing keys +in subviews, also stores them under the string keys. Finally, copies any +non-conflicting string keys from the subviews tables of the listed views.

      +

      Thus, doing something like this:

      +
      +self:addviews{
      +    Panel{
      +        view_id = 'panel',
      +        subviews = {
      +            Label{ view_id = 'label' }
      +        }
      +    }
      +}
      +
      +

      Would make the label accessible as both self.subviews.label and +self.subviews.panel.subviews.label.

      +
    • +
    • view:getWindowSize()

      +

      Returns the dimensions of the frame_body rectangle.

      +
    • +
    • view:getMousePos()

      +

      Returns the mouse x,y in coordinates local to the frame_body +rectangle if it is within its clip area, or nothing otherwise.

      +
    • +
    • view:updateLayout([parent_rect])

      +

      Recomputes layout of the view and its subviews. If no argument is +given, re-uses the previous parent rect. The process goes as follows:

      +
        +
      1. Calls preUpdateLayout(parent_rect) via invoke_before.
      2. +
      3. Uses computeFrame(parent_rect) to compute the desired frame.
      4. +
      5. Calls postComputeFrame(frame_body) via invoke_after.
      6. +
      7. Calls updateSubviewLayout(frame_body) to update children.
      8. +
      9. Calls postUpdateLayout(frame_body) via invoke_after.
      10. +
      +
    • +
    • view:computeFrame(parent_rect) (for overriding)

      +

      Called by updateLayout in order to compute the frame rectangle(s). +Should return the mkdims rectangle for the outer frame, and optionally +also for the body frame. If only one rectangle is returned, it is used +for both frames, and the margin becomes zero.

      +
    • +
    • view:updateSubviewLayout(frame_body)

      +

      Calls updateLayout on all children.

      +
    • +
    • view:render(painter)

      +

      Given the parent's painter, renders the view via the following process:

      +
        +
      1. Calls onRenderFrame(painter, frame_rect) to paint the outer frame.
      2. +
      3. Creates a new painter using the frame_body rect.
      4. +
      5. Calls onRenderBody(new_painter) to paint the client area.
      6. +
      7. Calls renderSubviews(new_painter) to paint visible children.
      8. +
      +
    • +
    • view:renderSubviews(painter)

      +

      Calls render on all visible subviews in the order they +appear in the subviews sequence.

      +
    • +
    • view:onRenderFrame(painter, rect) (for overriding)

      +

      Called by render to paint the outer frame; by default does nothing.

      +
    • +
    • view:onRenderBody(painter) (for overriding)

      +

      Called by render to paint the client area; by default does nothing.

      +
    • +
    • view:onInput(keys) (for overriding)

      +

      Override this to handle events. By default directly calls inputToSubviews. +Return a true value from this method to signal that the event has been handled +and should not be passed on to more views.

      +
    • +
    • view:inputToSubviews(keys)

      +

      Calls onInput on all visible active subviews, iterating the subviews +sequence in reverse order, so that topmost subviews get events first. +Returns true if any of the subviews handled the event.

      +
    • +
    +
    +
    +

    Screen class

    +

    This is a View subclass intended for use as a stand-alone dialog or screen. +It adds the following methods:

    +
      +
    • screen:isShown()

      +

      Returns true if the screen is currently in the game engine's display stack.

      +
    • +
    • screen:isDismissed()

      +

      Returns true if the screen is dismissed.

      +
    • +
    • screen:isActive()

      +

      Returns true if the screen is shown and not dismissed.

      +
    • +
    • screen:invalidate()

      +

      Requests a repaint. Note that currently using it is not necessary, because +repaints are constantly requested automatically, due to issues with native +screens happening otherwise.

      +
    • +
    • screen:renderParent()

      +

      Asks the parent native screen to render itself, or clears the screen if impossible.

      +
    • +
    • screen:sendInputToParent(...)

      +

      Uses simulateInput to send keypresses to the native parent screen.

      +
    • +
    • screen:show([parent])

      +

      Adds the screen to the display stack with the given screen as the parent; +if parent is not specified, places this one one topmost. Before calling +dfhack.screen.show, calls self:onAboutToShow(parent).

      +
    • +
    • screen:onAboutToShow(parent) (for overriding)

      +

      Called when dfhack.screen.show is about to be called.

      +
    • +
    • screen:onShow()

      +

      Called by dfhack.screen.show once the screen is successfully shown.

      +
    • +
    • screen:dismiss()

      +

      Dismisses the screen. A dismissed screen does not receive any more +events or paint requests, but may remain in the display stack for +a short time until the game removes it.

      +
    • +
    • screen:onDismiss() (for overriding)

      +

      Called by dfhack.screen.dismiss().

      +
    • +
    • screen:onDestroy() (for overriding)

      +

      Called by the native code when the screen is fully destroyed and removed +from the display stack. Place code that absolutely must be called whenever +the screen is removed by any means here.

      +
    • +
    • screen:onResize, screen:onRender

      +

      Defined as callbacks for native code.

      +
    • +
    +
    +
    +

    FramedScreen class

    +

    A Screen subclass that paints a visible frame around its body. +Most dialogs should inherit from this class.

    +

    A framed screen has the following attributes:

    + +++ + + + + + + + + + + + + + + +
    frame_style:A table that defines a set of pens to draw various parts of the frame.
    frame_title:A string to display in the middle of the top of the frame.
    frame_width:Desired width of the client area. If nil, the screen will occupy the whole width.
    frame_height:Likewise, for height.
    frame_inset:The gap between the frame and the client area. Defaults to 0.
    frame_background:
     The pen to fill in the frame with. Defaults to CLEAR_PEN.
    +

    There are the following predefined frame style tables:

    +
      +
    • GREY_FRAME

      +

      A plain grey-colored frame.

      +
    • +
    • BOUNDARY_FRAME

      +

      The same frame as used by the usual full-screen DF views, like dwarfmode.

      +
    • +
    • GREY_LINE_FRAME

      +

      A frame consisting of grey lines, similar to the one used by titan announcements.

      +
    • +
    +
    +
    +
    +

    gui.widgets

    +

    This module implements some basic widgets based on the View infrastructure.

    +
    +

    Widget class

    +

    Base of all the widgets. Inherits from View and has the following attributes:

    +
      +
    • frame = {...}

      +

      Specifies the constraints on the outer frame of the widget. +If omitted, the widget will occupy the whole parent rectangle.

      +

      The frame is specified as a table with the following possible fields:

      + +++ + + + + + + + + + + + + + + + + + +
      l:gap between the left edges of the frame and the parent.
      t:gap between the top edges of the frame and the parent.
      r:gap between the right edges of the frame and the parent.
      b:gap between the bottom edges of the frame and the parent.
      w:maximum width of the frame.
      h:maximum heigth of the frame.
      xalign:X alignment of the frame.
      yalign:Y alignment of the frame.
      +

      First the l,t,r,b fields restrict the available area for +placing the frame. If w and h are not specified or +larger then the computed area, it becomes the frame. Otherwise +the smaller frame is placed within the are based on the +xalign/yalign fields. If the align hints are omitted, they +are assumed to be 0, 1, or 0.5 based on which of the l/r/t/b +fields are set.

      +
    • +
    • frame_inset = {...}

      +

      Specifies the gap between the outer frame, and the client area. +The attribute may be a simple integer value to specify a uniform +inset, or a table with the following fields:

      + +++ + + + + + + + + + + + + + +
      l:left margin.
      t:top margin.
      r:right margin.
      b:bottom margin.
      x:left/right margin, if l and/or r are omitted.
      y:top/bottom margin, if t and/or b are omitted.
      +
    • +
    • frame_background = pen

      +

      The pen to fill the outer frame with. Defaults to no fill.

      +
    • +
    +
    +
    +

    Panel class

    +

    Inherits from Widget, and intended for grouping a number of subviews.

    +

    Has attributes:

    +
      +
    • subviews = {}

      +

      Used to initialize the subview list in the constructor.

      +
    • +
    • on_render = function(painter)

      +

      Called from onRenderBody.

      +
    • +
    +
    +
    +

    Pages class

    +

    Subclass of Panel; keeps exactly one child visible.

    +
      +
    • Pages{ ..., selected = ... }

      +

      Specifies which child to select initially; defaults to the first one.

      +
    • +
    • pages:getSelected()

      +

      Returns the selected index, child.

      +
    • +
    • pages:setSelected(index)

      +

      Selects the specified child, hiding the previous selected one. +It is permitted to use the subview object, or its view_id as index.

      +
    • +
    +
    +
    +

    EditField class

    +

    Subclass of Widget; implements a simple edit field.

    +

    Attributes:

    + +++ + + + + + + + + + + + +
    text:The current contents of the field.
    text_pen:The pen to draw the text with.
    on_char:Input validation callback; used as on_char(new_char,text). +If it returns false, the character is ignored.
    on_change:Change notification callback; used as on_change(new_text,old_text).
    on_submit:Enter key callback; if set the field will handle the key and call on_submit(text).
    +
    +
    +

    Label class

    +

    This Widget subclass implements flowing semi-static text.

    +

    It has the following attributes:

    + +++ + + + + + + + + + + + + + +
    text_pen:Specifies the pen for active text.
    text_dpen:Specifies the pen for disabled text.
    disabled:Boolean or a callback; if true, the label is disabled.
    enabled:Boolean or a callback; if false, the label is disabled.
    auto_height:Sets self.frame.h from the text height.
    auto_width:Sets self.frame.w from the text width.
    +

    The text itself is represented as a complex structure, and passed +to the object via the text argument of the constructor, or via +the setText method, as one of:

    +
      +
    • A simple string, possibly containing newlines.
    • +
    • A sequence of tokens.
    • +
    +

    Every token in the sequence in turn may be either a string, possibly +containing newlines, or a table with the following possible fields:

    +
      +
    • token.text = ...

      +

      Specifies the main text content of a token, and may be a string, or +a callback returning a string.

      +
    • +
    • token.gap = ...

      +

      Specifies the number of character positions to advance on the line +before rendering the token.

      +
    • +
    • token.tile = pen

      +

      Specifies a pen to paint as one tile before the main part of the token.

      +
    • +
    • token.key = '...'

      +

      Specifies the keycode associated with the token. The string description +of the key binding is added to the text content of the token.

      +
    • +
    • token.key_sep = '...'

      +

      Specifies the separator to place between the keybinding label produced +by token.key, and the main text of the token. If the separator is +'()', the token is formatted as text..' ('..binding..')'. Otherwise +it is simply binding..sep..text.

      +
    • +
    • token.enabled, token.disabled

      +

      Same as the attributes of the label itself, but applies only to the token.

      +
    • +
    • token.pen, token.dpen

      +

      Specify the pen and disabled pen to be used for the token's text. +The field may be either the pen itself, or a callback that returns it.

      +
    • +
    • token.on_activate

      +

      If this field is not nil, and token.key is set, the token will actually +respond to that key binding unless disabled, and call this callback. Eventually +this may be extended with mouse click support.

      +
    • +
    • token.id

      +

      Specifies a unique identifier for the token.

      +
    • +
    • token.line, token.x1, token.x2

      +

      Reserved for internal use.

      +
    • +
    +

    The Label widget implements the following methods:

    +
      +
    • label:setText(new_text)

      +

      Replaces the text currently contained in the widget.

      +
    • +
    • label:itemById(id)

      +

      Finds a token by its id field.

      +
    • +
    • label:getTextHeight()

      +

      Computes the height of the text.

      +
    • +
    • label:getTextWidth()

      +

      Computes the width of the text.

      +
    • +
    +
    +
    +

    List class

    +

    The List widget implements a simple list with paging.

    +

    It has the following attributes:

    + +++ + + + + + + + + + + + + + + + + + + + +
    text_pen:Specifies the pen for deselected list entries.
    cursor_pen:Specifies the pen for the selected entry.
    inactive_pen:If specified, used for the cursor when the widget is not active.
    icon_pen:Default pen for icons.
    on_select:Selection change callback; called as on_select(index,choice).
    on_submit:Enter key callback; if specified, the list reacts to the key +and calls it as on_submit(index,choice).
    row_height:Height of every row in text lines.
    icon_width:If not nil, the specified number of character columns +are reserved to the left of the list item for the icons.
    scroll_keys:Specifies which keys the list should react to as a table.
    +

    Every list item may be specified either as a string, or as a lua table +with the following fields:

    + +++ + + + + + + + + + + + + + +
    text:Specifies the label text in the same format as the Label text.
    caption, [1]:Deprecated legacy aliases for text.
    text_*:Reserved for internal use.
    key:Specifies a keybinding that acts as a shortcut for the specified item.
    icon:Specifies an icon string, or a pen to paint a single character. May be a callback.
    icon_pen:When the icon is a string, used to paint it.
    +

    The list supports the following methods:

    +
      +
    • List{ ..., choices = ..., selected = ... }

      +

      Same as calling setChoices after construction.

      +
    • +
    • list:setChoices(choices[, selected])

      +

      Replaces the list of choices, possibly also setting the currently selected index.

      +
    • +
    • list:setSelected(selected)

      +

      Sets the currently selected index. Returns the index after validation.

      +
    • +
    • list:getChoices()

      +

      Returns the list of choices.

      +
    • +
    • list:getSelected()

      +

      Returns the selected index, choice, or nothing if the list is empty.

      +
    • +
    • list:getContentWidth()

      +

      Returns the minimal width to draw all choices without clipping.

      +
    • +
    • list:getContentHeight()

      +

      Returns the minimal width to draw all choices without scrolling.

      +
    • +
    • list:submit()

      +

      Call the on_submit callback, as if the Enter key was handled.

      +
    • +
    +
    +
    +

    FilteredList class

    +

    This widget combines List, EditField and Label into a combo-box like +construction that allows filtering the list by subwords of its items.

    +

    In addition to passing through all attributes supported by List, it +supports:

    + +++ + + + + + + +
    edit_pen:If specified, used instead of cursor_pen for the edit field.
    not_found_label:
     Specifies the text of the label shown when no items match the filter.
    +

    The list choices may include the following attributes:

    + +++ + + + +
    search_key:If specified, used instead of text to match against the filter.
    +

    The widget implements:

    +
      +
    • list:setChoices(choices[, selected])

      +

      Resets the filter, and passes through to the inner list.

      +
    • +
    • list:getChoices()

      +

      Returns the list of all choices.

      +
    • +
    • list:getFilter()

      +

      Returns the current filter string, and the filtered list of choices.

      +
    • +
    • list:setFilter(filter[,pos])

      +

      Sets the new filter string, filters the list, and selects the item at +index pos in the unfiltered list if possible.

      +
    • +
    • list:canSubmit()

      +

      Checks if there are currently any choices in the filtered list.

      +
    • +
    • list:getSelected(), list:getContentWidth(), list:getContentHeight(), list:submit()

      +

      Same as with an ordinary list.

      +
    • +
    +
    +
    +
    -

    Plugins

    +

    Plugins

    DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.

    The following plugins have lua support.

    -

    burrows

    +

    burrows

    Implements extended burrow manipulations.

    Events:

      @@ -2169,13 +2978,13 @@ set is the same as used by the command line.

      The lua module file also re-exports functions from dfhack.burrows.

    -

    sort

    +

    sort

    Does not export any native functions as of now. Instead, it calls lua code to perform the actual ordering of list items.

    -

    Scripts

    +

    Scripts

    Any files with the .lua extension placed into hack/scripts/* are automatically used by the DFHack core as commands. The matching command name consists of the name of the file sans diff --git a/Lua API.rst b/Lua API.rst index ec48938db..d06e3d2e6 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1441,6 +1441,11 @@ Basic painting functions: Requests repaint of the screen by setting a flag. Unlike other functions in this section, this may be used at any time. +* ``dfhack.screen.getKeyDisplay(key)`` + + Returns the string that should be used to represent the given + logical keybinding on the screen in texts like "press Key to ...". + The "pen" argument used by functions above may be represented by a table with the following possible fields: @@ -1796,6 +1801,11 @@ environment by the mandatory init file dfhack.lua: safecall, qerror, mkmodule, reload +* Miscellaneous constants + + :NEWLINE, COMMA, PERIOD: evaluate to the relevant character strings. + :DEFAULT_NIL: is an unspecified unique token used by the class module below. + * ``printall(obj)`` If the argument is a lua table or DF object reference, prints all fields. @@ -1906,12 +1916,29 @@ utils as a guide to which values should be skipped as uninteresting. The ``force`` argument makes it always return a non-*nil* value. +* ``utils.parse_bitfield_int(value, type_ref)`` + + Given an int ``value``, and a bitfield type in the ``df`` tree, + it returns a lua table mapping the enabled bit keys to *true*, + unless value is 0, in which case it returns *nil*. + +* ``utils.list_bitfield_flags(bitfield[, list])`` + + Adds all enabled bitfield keys to ``list`` or a newly-allocated + empty sequence, and returns it. The ``bitfield`` argument may + be *nil*. + * ``utils.sort_vector(vector,field,cmpfun)`` Sorts a native vector or lua sequence using the comparator function. If ``field`` is not *nil*, applies the comparator to the field instead of the whole object. +* ``utils.linear_index(vector,key[,field])`` + + Searches for ``key`` in the vector, and returns *index, found_value*, + or *nil* if none found. + * ``utils.binsearch(vector,key,field,cmpfun,min,max)`` Does a binary search in a native vector or lua sequence for @@ -1947,6 +1974,23 @@ utils Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``. +* ``utils.call_with_string(obj,methodname,...)`` + + Allocates a temporary string object, calls ``obj:method(tmp,...)``, and + returns the value written into the temporary after deleting it. + +* ``utils.getBuildingName(building)`` + + Returns the string description of the given building. + +* ``utils.getBuildingCenter(building)`` + + Returns an x/y/z table pointing at the building center. + +* ``utils.split_string(string, delimiter)`` + + Splits the string by the given delimiter, and returns a sequence of results. + * ``utils.prompt_yes_no(prompt, default)`` Presents a yes/no prompt to the user. If ``default`` is not *nil*, @@ -2006,19 +2050,32 @@ Implements a trivial single-inheritance class system. If the default value should be *nil*, use ``ATTRS { foo = DEFAULT_NIL }``. + Declaring an attribute is mostly the same as defining your ``init`` method like this:: + + function Class.init(args) + self.attr1 = args.attr1 or default1 + self.attr2 = args.attr2 or default2 + ... + end + + The main difference is that attributes are processed as a separate + initialization step, before any ``init`` methods are called. They + also make the directy relation between instance fields and constructor + arguments more explicit. + * ``new_obj = Class{ foo = arg, bar = arg, ... }`` Calling the class as a function creates and initializes a new instance. Initialization happens in this order: 1. An empty instance table is created, and its metatable set. - 2. The ``preinit`` method is called via ``invoke_before`` (see below) - with the table used as argument to the class. This method is intended + 2. The ``preinit`` methods are called via ``invoke_before`` (see below) + with the table used as argument to the class. These methods are intended for validating and tweaking that argument table. 3. Declared ATTRS are initialized from the argument table or their default values. - 4. The ``init`` method is called via ``invoke_after`` with the argument table. + 4. The ``init`` methods are called via ``invoke_after`` with the argument table. This is the main constructor method. - 5. The ``postinit`` method is called via ``invoke_after`` with the argument table. + 5. The ``postinit`` methods are called via ``invoke_after`` with the argument table. Place code that should be called after the object is fully constructed here. Predefined instance methods: @@ -2033,6 +2090,14 @@ Predefined instance methods: properly passing in self, and optionally a number of initial arguments too. The arguments given to the closure are appended to these. +* ``instance:cb_getfield(field_name)`` + + Returns a closure that returns the specified field of the object when called. + +* ``instance:cb_setfield(field_name)`` + + Returns a closure that sets the specified field to its argument when called. + * ``instance:invoke_before(method_name, args...)`` Navigates the inheritance chain of the instance starting from the most specific @@ -2057,6 +2122,715 @@ Predefined instance methods: To avoid confusion, these methods cannot be redefined. +================== +In-game UI Library +================== + +A number of lua modules with names starting with ``gui`` are dedicated +to wrapping the natives of the ``dfhack.screen`` module in a way that +is easy to use. This allows relatively easily and naturally creating +dialogs that integrate in the main game UI window. + +These modules make extensive use of the ``class`` module, and define +things ranging from the basic ``Painter``, ``View`` and ``Screen`` +classes, to fully functional predefined dialogs. + +gui +=== + +This module defines the most important classes and functions for +implementing interfaces. This documents those of them that are +considered stable. + + +Misc +---- + +* ``USE_GRAPHICS`` + + Contains the value of ``dfhack.screen.inGraphicsMode()``, which cannot be + changed without restarting the game and thus is constant during the session. + +* ``CLEAR_PEN`` + + The black pen used to clear the screen. + +* ``simulateInput(screen, keys...)`` + + This function wraps an undocumented native function that passes a set of + keycodes to a screen, and is the official way to do that. + + Every argument after the initial screen may be *nil*, a numeric keycode, + a string keycode, a sequence of numeric or string keycodes, or a mapping + of keycodes to *true* or *false*. For instance, it is possible to use the + table passed as argument to ``onInput``. + +* ``mkdims_xy(x1,y1,x2,y2)`` + + Returns a table containing the arguments as fields, and also ``width`` and + ``height`` that contains the rectangle dimensions. + +* ``mkdims_wh(x1,y1,width,height)`` + + Returns the same kind of table as ``mkdims_xy``, only this time it computes + ``x2`` and ``y2``. + +* ``is_in_rect(rect,x,y)`` + + Checks if the given point is within a rectangle, represented by a table produced + by one of the ``mkdims`` functions. + +* ``blink_visible(delay)`` + + Returns *true* or *false*, with the value switching to the opposite every ``delay`` + msec. This is intended for rendering blinking interface objects. + +* ``getKeyDisplay(keycode)`` + + Wraps ``dfhack.screen.getKeyDisplay`` in order to allow using strings for the keycode argument. + + +ViewRect class +-------------- + +This class represents an on-screen rectangle with an associated independent +clip area rectangle. It is the base of the ``Painter`` class, and is used by +``Views`` to track their client area. + +* ``ViewRect{ rect = ..., clip_rect = ..., view_rect = ..., clip_view = ... }`` + + The constructor has the following arguments: + + :rect: The ``mkdims`` rectangle in screen coordinates of the logical viewport. + Defaults to the whole screen. + :clip_rect: The clip rectangle in screen coordinates. Defaults to ``rect``. + :view_rect: A ViewRect object to copy from; overrides both ``rect`` and ``clip_rect``. + :clip_view: A ViewRect object to intersect the specified clip area with. + +* ``rect:isDefunct()`` + + Returns *true* if the clip area is empty, i.e. no painting is possible. + +* ``rect:inClipGlobalXY(x,y)`` + + Checks if these global coordinates are within the clip rectangle. + +* ``rect:inClipLocalXY(x,y)`` + + Checks if these coordinates (specified relative to ``x1,y1``) are within the clip rectangle. + +* ``rect:localXY(x,y)`` + + Converts a pair of global coordinates to local; returns *x_local,y_local*. + +* ``rect:globalXY(x,y)`` + + Converts a pair of local coordinates to global; returns *x_global,y_global*. + +* ``rect:viewport(x,y,w,h)`` or ``rect:viewport(subrect)`` + + Returns a ViewRect representing a sub-rectangle of the current one. + The arguments are specified in local coordinates; the ``subrect`` + argument must be a ``mkdims`` table. The returned object consists of + the exact specified rectangle, and a clip area produced by intersecting + it with the clip area of the original object. + + +Painter class +------------- + +The painting natives in ``dfhack.screen`` apply to the whole screen, are +completely stateless and don't implement clipping. + +The Painter class inherits from ViewRect to provide clipping and local +coordinates, and tracks current cursor position and current pen. + +* ``Painter{ ..., pen = ..., key_pen = ... }`` + + In addition to ViewRect arguments, Painter accepts a suggestion of + the initial value for the main pen, and the keybinding pen. They + default to COLOR_GREY and COLOR_LIGHTGREEN otherwise. + + There are also some convenience functions that wrap this constructor: + + - ``Painter.new(rect,pen)`` + - ``Painter.new_view(view_rect,pen)`` + - ``Painter.new_xy(x1,y1,x2,y2,pen)`` + - ``Painter.new_wh(x1,y1,width,height,pen)`` + +* ``painter:isValidPos()`` + + Checks if the current cursor position is within the clip area. + +* ``painter:viewport(x,y,w,h)`` + + Like the superclass method, but returns a Painter object. + +* ``painter:cursor()`` + + Returns the current cursor *x,y* in local coordinates. + +* ``painter:seek(x,y)`` + + Sets the current cursor position, and returns *self*. + Either of the arguments may be *nil* to keep the current value. + +* ``painter:advance(dx,dy)`` + + Adds the given offsets to the cursor position, and returns *self*. + Either of the arguments may be *nil* to keep the current value. + +* ``painter:newline([dx])`` + + Advances the cursor to the start of the next line plus the given x offset, and returns *self*. + +* ``painter:pen(...)`` + + Sets the current pen to ``dfhack.pen.parse(old_pen,...)``, and returns *self*. + +* ``painter:key_pen(...)`` + + Sets the current keybinding pen to ``dfhack.pen.parse(old_pen,...)``, and returns *self*. + +* ``painter:clear()`` + + Fills the whole clip rectangle with ``CLEAR_PEN``, and returns *self*. + +* ``painter:fill(x1,y1,x2,y2[,...])`` or ``painter:fill(rect[,...])`` + + Fills the specified local coordinate rectangle with ``dfhack.pen.parse(cur_pen,...)``, + and returns *self*. + +* ``painter:char([char[, ...]])`` + + Paints one character using ``char`` and ``dfhack.pen.parse(cur_pen,...)``; returns *self*. + The ``char`` argument, if not nil, is used to override the ``ch`` property of the pen. + +* ``painter:tile([char, tile[, ...]])`` + + Like above, but also allows overriding the ``tile`` property on ad-hoc basis. + +* ``painter:string(text[, ...])`` + + Paints the string with ``dfhack.pen.parse(cur_pen,...)``; returns *self*. + +* ``painter:key(keycode[, ...])`` + + Paints the description of the keycode using ``dfhack.pen.parse(cur_key_pen,...)``; returns *self*. + +As noted above, all painting methods return *self*, in order to allow chaining them like this:: + + painter:pen(foo):seek(x,y):char(1):advance(1):string('bar')... + + +View class +---------- + +This class is the common abstract base of both the stand-alone screens +and common widgets to be used inside them. It defines the basic layout, +rendering and event handling framework. + +The class defines the following attributes: + +:visible: Specifies that the view should be painted. +:active: Specifies that the view should receive events, if also visible. +:view_id: Specifies an identifier to easily identify the view among subviews. + This is reserved for implementation of top-level views, and should + not be used by widgets for their internal subviews. + +It also always has the following fields: + +:subviews: Contains a table of all subviews. The sequence part of the + table is used for iteration. In addition, subviews are also + indexed under their *view_id*, if any; see ``addviews()`` below. + +These fields are computed by the layout process: + +:frame_parent_rect: The ViewRect represeting the client area of the parent view. +:frame_rect: The ``mkdims`` rect of the outer frame in parent-local coordinates. +:frame_body: The ViewRect representing the body part of the View's own frame. + +The class has the following methods: + +* ``view:addviews(list)`` + + Adds the views in the list to the ``subviews`` sequence. If any of the views + in the list have ``view_id`` attributes that don't conflict with existing keys + in ``subviews``, also stores them under the string keys. Finally, copies any + non-conflicting string keys from the ``subviews`` tables of the listed views. + + Thus, doing something like this:: + + self:addviews{ + Panel{ + view_id = 'panel', + subviews = { + Label{ view_id = 'label' } + } + } + } + + Would make the label accessible as both ``self.subviews.label`` and + ``self.subviews.panel.subviews.label``. + +* ``view:getWindowSize()`` + + Returns the dimensions of the ``frame_body`` rectangle. + +* ``view:getMousePos()`` + + Returns the mouse *x,y* in coordinates local to the ``frame_body`` + rectangle if it is within its clip area, or nothing otherwise. + +* ``view:updateLayout([parent_rect])`` + + Recomputes layout of the view and its subviews. If no argument is + given, re-uses the previous parent rect. The process goes as follows: + + 1. Calls ``preUpdateLayout(parent_rect)`` via ``invoke_before``. + 2. Uses ``computeFrame(parent_rect)`` to compute the desired frame. + 3. Calls ``postComputeFrame(frame_body)`` via ``invoke_after``. + 4. Calls ``updateSubviewLayout(frame_body)`` to update children. + 5. Calls ``postUpdateLayout(frame_body)`` via ``invoke_after``. + +* ``view:computeFrame(parent_rect)`` *(for overriding)* + + Called by ``updateLayout`` in order to compute the frame rectangle(s). + Should return the ``mkdims`` rectangle for the outer frame, and optionally + also for the body frame. If only one rectangle is returned, it is used + for both frames, and the margin becomes zero. + +* ``view:updateSubviewLayout(frame_body)`` + + Calls ``updateLayout`` on all children. + +* ``view:render(painter)`` + + Given the parent's painter, renders the view via the following process: + + 1. Calls ``onRenderFrame(painter, frame_rect)`` to paint the outer frame. + 2. Creates a new painter using the ``frame_body`` rect. + 3. Calls ``onRenderBody(new_painter)`` to paint the client area. + 4. Calls ``renderSubviews(new_painter)`` to paint visible children. + +* ``view:renderSubviews(painter)`` + + Calls ``render`` on all ``visible`` subviews in the order they + appear in the ``subviews`` sequence. + +* ``view:onRenderFrame(painter, rect)`` *(for overriding)* + + Called by ``render`` to paint the outer frame; by default does nothing. + +* ``view:onRenderBody(painter)`` *(for overriding)* + + Called by ``render`` to paint the client area; by default does nothing. + +* ``view:onInput(keys)`` *(for overriding)* + + Override this to handle events. By default directly calls ``inputToSubviews``. + Return a true value from this method to signal that the event has been handled + and should not be passed on to more views. + +* ``view:inputToSubviews(keys)`` + + Calls ``onInput`` on all visible active subviews, iterating the ``subviews`` + sequence in *reverse order*, so that topmost subviews get events first. + Returns *true* if any of the subviews handled the event. + + +Screen class +------------ + +This is a View subclass intended for use as a stand-alone dialog or screen. +It adds the following methods: + +* ``screen:isShown()`` + + Returns *true* if the screen is currently in the game engine's display stack. + +* ``screen:isDismissed()`` + + Returns *true* if the screen is dismissed. + +* ``screen:isActive()`` + + Returns *true* if the screen is shown and not dismissed. + +* ``screen:invalidate()`` + + Requests a repaint. Note that currently using it is not necessary, because + repaints are constantly requested automatically, due to issues with native + screens happening otherwise. + +* ``screen:renderParent()`` + + Asks the parent native screen to render itself, or clears the screen if impossible. + +* ``screen:sendInputToParent(...)`` + + Uses ``simulateInput`` to send keypresses to the native parent screen. + +* ``screen:show([parent])`` + + Adds the screen to the display stack with the given screen as the parent; + if parent is not specified, places this one one topmost. Before calling + ``dfhack.screen.show``, calls ``self:onAboutToShow(parent)``. + +* ``screen:onAboutToShow(parent)`` *(for overriding)* + + Called when ``dfhack.screen.show`` is about to be called. + +* ``screen:onShow()`` + + Called by ``dfhack.screen.show`` once the screen is successfully shown. + +* ``screen:dismiss()`` + + Dismisses the screen. A dismissed screen does not receive any more + events or paint requests, but may remain in the display stack for + a short time until the game removes it. + +* ``screen:onDismiss()`` *(for overriding)* + + Called by ``dfhack.screen.dismiss()``. + +* ``screen:onDestroy()`` *(for overriding)* + + Called by the native code when the screen is fully destroyed and removed + from the display stack. Place code that absolutely must be called whenever + the screen is removed by any means here. + +* ``screen:onResize``, ``screen:onRender`` + + Defined as callbacks for native code. + + +FramedScreen class +------------------ + +A Screen subclass that paints a visible frame around its body. +Most dialogs should inherit from this class. + +A framed screen has the following attributes: + +:frame_style: A table that defines a set of pens to draw various parts of the frame. +:frame_title: A string to display in the middle of the top of the frame. +:frame_width: Desired width of the client area. If *nil*, the screen will occupy the whole width. +:frame_height: Likewise, for height. +:frame_inset: The gap between the frame and the client area. Defaults to 0. +:frame_background: The pen to fill in the frame with. Defaults to CLEAR_PEN. + +There are the following predefined frame style tables: + +* ``GREY_FRAME`` + + A plain grey-colored frame. + +* ``BOUNDARY_FRAME`` + + The same frame as used by the usual full-screen DF views, like dwarfmode. + +* ``GREY_LINE_FRAME`` + + A frame consisting of grey lines, similar to the one used by titan announcements. + + +gui.widgets +=========== + +This module implements some basic widgets based on the View infrastructure. + +Widget class +------------ + +Base of all the widgets. Inherits from View and has the following attributes: + +* ``frame = {...}`` + + Specifies the constraints on the outer frame of the widget. + If omitted, the widget will occupy the whole parent rectangle. + + The frame is specified as a table with the following possible fields: + + :l: gap between the left edges of the frame and the parent. + :t: gap between the top edges of the frame and the parent. + :r: gap between the right edges of the frame and the parent. + :b: gap between the bottom edges of the frame and the parent. + :w: maximum width of the frame. + :h: maximum heigth of the frame. + :xalign: X alignment of the frame. + :yalign: Y alignment of the frame. + + First the ``l,t,r,b`` fields restrict the available area for + placing the frame. If ``w`` and ``h`` are not specified or + larger then the computed area, it becomes the frame. Otherwise + the smaller frame is placed within the are based on the + ``xalign/yalign`` fields. If the align hints are omitted, they + are assumed to be 0, 1, or 0.5 based on which of the ``l/r/t/b`` + fields are set. + +* ``frame_inset = {...}`` + + Specifies the gap between the outer frame, and the client area. + The attribute may be a simple integer value to specify a uniform + inset, or a table with the following fields: + + :l: left margin. + :t: top margin. + :r: right margin. + :b: bottom margin. + :x: left/right margin, if ``l`` and/or ``r`` are omitted. + :y: top/bottom margin, if ``t`` and/or ``b`` are omitted. + +* ``frame_background = pen`` + + The pen to fill the outer frame with. Defaults to no fill. + +Panel class +----------- + +Inherits from Widget, and intended for grouping a number of subviews. + +Has attributes: + +* ``subviews = {}`` + + Used to initialize the subview list in the constructor. + +* ``on_render = function(painter)`` + + Called from ``onRenderBody``. + +Pages class +----------- + +Subclass of Panel; keeps exactly one child visible. + +* ``Pages{ ..., selected = ... }`` + + Specifies which child to select initially; defaults to the first one. + +* ``pages:getSelected()`` + + Returns the selected *index, child*. + +* ``pages:setSelected(index)`` + + Selects the specified child, hiding the previous selected one. + It is permitted to use the subview object, or its ``view_id`` as index. + +EditField class +--------------- + +Subclass of Widget; implements a simple edit field. + +Attributes: + +:text: The current contents of the field. +:text_pen: The pen to draw the text with. +:on_char: Input validation callback; used as ``on_char(new_char,text)``. + If it returns false, the character is ignored. +:on_change: Change notification callback; used as ``on_change(new_text,old_text)``. +:on_submit: Enter key callback; if set the field will handle the key and call ``on_submit(text)``. + +Label class +----------- + +This Widget subclass implements flowing semi-static text. + +It has the following attributes: + +:text_pen: Specifies the pen for active text. +:text_dpen: Specifies the pen for disabled text. +:disabled: Boolean or a callback; if true, the label is disabled. +:enabled: Boolean or a callback; if false, the label is disabled. +:auto_height: Sets self.frame.h from the text height. +:auto_width: Sets self.frame.w from the text width. + +The text itself is represented as a complex structure, and passed +to the object via the ``text`` argument of the constructor, or via +the ``setText`` method, as one of: + +* A simple string, possibly containing newlines. +* A sequence of tokens. + +Every token in the sequence in turn may be either a string, possibly +containing newlines, or a table with the following possible fields: + +* ``token.text = ...`` + + Specifies the main text content of a token, and may be a string, or + a callback returning a string. + +* ``token.gap = ...`` + + Specifies the number of character positions to advance on the line + before rendering the token. + +* ``token.tile = pen`` + + Specifies a pen to paint as one tile before the main part of the token. + +* ``token.key = '...'`` + + Specifies the keycode associated with the token. The string description + of the key binding is added to the text content of the token. + +* ``token.key_sep = '...'`` + + Specifies the separator to place between the keybinding label produced + by ``token.key``, and the main text of the token. If the separator is + '()', the token is formatted as ``text..' ('..binding..')'``. Otherwise + it is simply ``binding..sep..text``. + +* ``token.enabled``, ``token.disabled`` + + Same as the attributes of the label itself, but applies only to the token. + +* ``token.pen``, ``token.dpen`` + + Specify the pen and disabled pen to be used for the token's text. + The field may be either the pen itself, or a callback that returns it. + +* ``token.on_activate`` + + If this field is not nil, and ``token.key`` is set, the token will actually + respond to that key binding unless disabled, and call this callback. Eventually + this may be extended with mouse click support. + +* ``token.id`` + + Specifies a unique identifier for the token. + +* ``token.line``, ``token.x1``, ``token.x2`` + + Reserved for internal use. + +The Label widget implements the following methods: + +* ``label:setText(new_text)`` + + Replaces the text currently contained in the widget. + +* ``label:itemById(id)`` + + Finds a token by its ``id`` field. + +* ``label:getTextHeight()`` + + Computes the height of the text. + +* ``label:getTextWidth()`` + + Computes the width of the text. + +List class +---------- + +The List widget implements a simple list with paging. + +It has the following attributes: + +:text_pen: Specifies the pen for deselected list entries. +:cursor_pen: Specifies the pen for the selected entry. +:inactive_pen: If specified, used for the cursor when the widget is not active. +:icon_pen: Default pen for icons. +:on_select: Selection change callback; called as ``on_select(index,choice)``. +:on_submit: Enter key callback; if specified, the list reacts to the key + and calls it as ``on_submit(index,choice)``. +:row_height: Height of every row in text lines. +:icon_width: If not *nil*, the specified number of character columns + are reserved to the left of the list item for the icons. +:scroll_keys: Specifies which keys the list should react to as a table. + +Every list item may be specified either as a string, or as a lua table +with the following fields: + +:text: Specifies the label text in the same format as the Label text. +:caption, [1]: Deprecated legacy aliases for **text**. +:text_*: Reserved for internal use. +:key: Specifies a keybinding that acts as a shortcut for the specified item. +:icon: Specifies an icon string, or a pen to paint a single character. May be a callback. +:icon_pen: When the icon is a string, used to paint it. + +The list supports the following methods: + +* ``List{ ..., choices = ..., selected = ... }`` + + Same as calling ``setChoices`` after construction. + +* ``list:setChoices(choices[, selected])`` + + Replaces the list of choices, possibly also setting the currently selected index. + +* ``list:setSelected(selected)`` + + Sets the currently selected index. Returns the index after validation. + +* ``list:getChoices()`` + + Returns the list of choices. + +* ``list:getSelected()`` + + Returns the selected *index, choice*, or nothing if the list is empty. + +* ``list:getContentWidth()`` + + Returns the minimal width to draw all choices without clipping. + +* ``list:getContentHeight()`` + + Returns the minimal width to draw all choices without scrolling. + +* ``list:submit()`` + + Call the ``on_submit`` callback, as if the Enter key was handled. + +FilteredList class +------------------ + +This widget combines List, EditField and Label into a combo-box like +construction that allows filtering the list by subwords of its items. + +In addition to passing through all attributes supported by List, it +supports: + +:edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. +:not_found_label: Specifies the text of the label shown when no items match the filter. + +The list choices may include the following attributes: + +:search_key: If specified, used instead of **text** to match against the filter. + +The widget implements: + +* ``list:setChoices(choices[, selected])`` + + Resets the filter, and passes through to the inner list. + +* ``list:getChoices()`` + + Returns the list of *all* choices. + +* ``list:getFilter()`` + + Returns the current filter string, and the *filtered* list of choices. + +* ``list:setFilter(filter[,pos])`` + + Sets the new filter string, filters the list, and selects the item at + index ``pos`` in the *unfiltered* list if possible. + +* ``list:canSubmit()`` + + Checks if there are currently any choices in the filtered list. + +* ``list:getSelected()``, ``list:getContentWidth()``, ``list:getContentHeight()``, ``list:submit()`` + + Same as with an ordinary list. + ======= Plugins diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index e731af068..ad408a2ea 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -504,7 +504,7 @@ function List:onRenderBody(dc) local icon = getval(obj.icon) if icon then dc:seek(0, y) - if type(icon) == 'table' then + if type(icon) ~= 'string' then dc:char(nil,icon) else if current then @@ -573,7 +573,7 @@ FilteredList = defclass(FilteredList, Widget) function FilteredList:init(info) self.edit = EditField{ - text_pen = info.cursor_pen, + text_pen = info.edit_pen or info.cursor_pen, frame = { l = info.icon_width, t = 0 }, on_change = self:callback('onFilterChange'), on_char = self:callback('onFilterChar'), @@ -583,6 +583,7 @@ function FilteredList:init(info) text_pen = info.text_pen, cursor_pen = info.cursor_pen, inactive_pen = info.inactive_pen, + icon_pen = info.icon_pen, row_height = info.row_height, scroll_keys = info.scroll_keys, icon_width = info.icon_width, diff --git a/library/lua/utils.lua b/library/lua/utils.lua index e7267038c..6d6b4de5c 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -457,6 +457,7 @@ function getBuildingCenter(building) return xyz2pos(building.centerx, building.centery, building.z) end +-- Split the string by the given delimiter function split_string(self, delimiter) local result = { } local from = 1 From edf80ff748ad8193015a98936e5ac86263bf9ac7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 4 Nov 2012 18:03:02 +0400 Subject: [PATCH 49/79] Show the sex of the unit in the bottom line of Dwarf Manipulator. --- plugins/manipulator.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 1bc0195b6..79999d468 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -1067,18 +1067,22 @@ void viewscreen_unitlaborsst::render() if (cur != NULL) { df::unit *unit = cur->unit; - int x = 1; - Screen::paintString(Screen::Pen(' ', 15, 0), x, 3 + num_rows + 2, cur->transname); + int x = 1, y = 3 + num_rows + 2; + Screen::Pen white_pen(' ', 15, 0); + + Screen::paintString(white_pen, x, y, (cur->unit && cur->unit->sex) ? "\x0b" : "\x0c"); + x += 2; + Screen::paintString(white_pen, x, y, cur->transname); x += cur->transname.length(); if (cur->transname.length()) { - Screen::paintString(Screen::Pen(' ', 15, 0), x, 3 + num_rows + 2, ", "); + Screen::paintString(white_pen, x, y, ", "); x += 2; } - Screen::paintString(Screen::Pen(' ', 15, 0), x, 3 + num_rows + 2, cur->profession); + Screen::paintString(white_pen, x, y, cur->profession); x += cur->profession.length(); - Screen::paintString(Screen::Pen(' ', 15, 0), x, 3 + num_rows + 2, ": "); + Screen::paintString(white_pen, x, y, ": "); x += 2; string str; From 20e98d49263c2e1d719a57fc3c138dfafb32fd8f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 4 Nov 2012 20:51:13 +0400 Subject: [PATCH 50/79] Add a script for viewing and poking at local populations. --- scripts/region-pops.lua | 141 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 scripts/region-pops.lua diff --git a/scripts/region-pops.lua b/scripts/region-pops.lua new file mode 100644 index 000000000..779167134 --- /dev/null +++ b/scripts/region-pops.lua @@ -0,0 +1,141 @@ +-- Shows populations of animals in the region, and allows tweaking them. + +local utils = require 'utils' + +local function sort_keys(tab) + local kt = {} + for k,v in pairs(tab) do table.insert(kt,k) end + table.sort(kt) + return ipairs(kt) +end + +local is_plant_map = { + Animal = false, Vermin = false, VerminInnumerable = false, + ColonyInsect = false, Tree = true, Grass = true, Bush = true +} + +function enum_populations() + local stat_table = { + plants = {}, + creatures = {}, + any = {} + } + + for i,v in ipairs(df.global.world.populations) do + local typeid = df.world_population_type[v.type] + local is_plant = is_plant_map[typeid] + local id, obj, otable, idtoken + + if is_plant then + id = v.plant + obj = df.plant_raw.find(id) + otable = stat_table.plants + idtoken = obj.id + else + id = v.race + obj = df.creature_raw.find(id) + otable = stat_table.creatures + idtoken = obj.creature_id + end + + local entry = otable[idtoken] + if not entry then + entry = { + obj = obj, token = idtoken, id = id, records = {}, + count = 0, known_count = 0, + known = false, infinite = false + } + otable[idtoken] = entry + stat_table.any[idtoken] = entry + end + + table.insert(entry.records, v) + entry.known = entry.known or v.known + + if v.quantity < 10000001 then + entry.count = entry.count + v.quantity + if v.known then + entry.known_count = entry.known_count + v.quantity + end + else + entry.infinite = true + end + end + + return stat_table +end + +function list_poptable(entries, all, pattern) + for _,k in sort_keys(entries) do + local entry = entries[k] + if (all or entry.known) and (not pattern or string.match(k,pattern)) then + local count = entry.known_count + if all then + count = entry.count + end + if entry.infinite then + count = 'innumerable' + end + print(string.format('%-40s %s', entry.token, count)) + end + end +end + +function list_populations(stat_table, all, pattern) + print('Plants:') + list_poptable(stat_table.plants, true, pattern) + print('\nCreatures and vermin:') + list_poptable(stat_table.creatures, all, pattern) +end + + +function boost_population(entry, factor, boost_count) + for _,v in ipairs(entry.records) do + if v.quantity < 10000001 then + boost_count = boost_count + 1 + v.quantity = math.floor(v.quantity * factor) + end + end + return boost_count +end + +local args = {...} +local pops = enum_populations() + +if args[1] == 'list' or args[1] == 'list-all' then + list_populations(pops, args[1] == 'list-all', args[2]) +elseif args[1] == 'boost' or args[1] == 'boost-all' then + local factor = tonumber(args[3]) + if not factor or factor < 0 then + qerror('Invalid boost factor.') + end + + local count = 0 + + if args[1] == 'boost' then + local entry = pops.any[args[2]] or qerror('Unknown population token.') + count = boost_population(entry, factor, count) + else + for k,entry in pairs(pops.any) do + if string.match(k, args[2]) then + count = boost_population(entry, factor, count) + end + end + end + + print('Updated '..count..' populations.') +else + print([[ +Usage: + region-pops list [pattern] + Lists encountered populations of the region, possibly restricted by pattern. + region-pops list-all [pattern] + Lists all populations of the region. + region-pops boost + Multiply all populations of TOKEN by factor. + If the factor is greater than one, increases the + population, otherwise decreases it. + region-pops boost-all + Same as above, but match using a pattern acceptable to list. +]]) +end From ff982dcf738ba766270663d8fc751c55c1fe8699 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 7 Nov 2012 13:31:36 +0400 Subject: [PATCH 51/79] Fix representation and parsing of built-in materials. The trick is to support both FOO and FOO:NONE for all of them, including INORGANIC[:NONE]. Otherwise the workflow gui scripts have problems. --- NEWS | 2 ++ dfhack.init-example | 2 +- library/modules/Materials.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index d5080b05a..f2237cab9 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ DFHack future - fastdwarf: new mode using debug flags, and some internal consistency fixes. - added a small stand-alone utility for applying and removing binary patches. - removebadthoughts: add --dry-run option + New scripts: + - region-pops: displays animal populations of the region and allows tweaking them. New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. diff --git a/dfhack.init-example b/dfhack.init-example index d7f3f5399..20048e39e 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -18,7 +18,7 @@ keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave # gui/rename script keybinding add Ctrl-Shift-N gui/rename -keybinding add Alt-Shift-P "gui/rename unit-profession" +keybinding add Ctrl-Shift-T "gui/rename unit-profession" ############################## # Generic adv mode bindings # diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 454fdf66d..7c06aeb4c 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -190,6 +190,8 @@ bool MaterialInfo::find(const std::vector &items) } else if (items.size() == 2) { + if (items[1] == "NONE" && findBuiltin(items[0])) + return true; if (findPlant(items[0], items[1])) return true; if (findCreature(items[0], items[1])) @@ -210,7 +212,7 @@ bool MaterialInfo::findBuiltin(const std::string &token) } df::world_raws &raws = world->raws; - for (int i = 1; i < NUM_BUILTIN; i++) + for (int i = 0; i < NUM_BUILTIN; i++) { auto obj = raws.mat_table.builtin[i]; if (obj && obj->id == token) @@ -312,7 +314,7 @@ std::string MaterialInfo::getToken() else if (index == 1) return "COAL:CHARCOAL"; } - return material->id + ":NONE"; + return material->id; case Inorganic: return "INORGANIC:" + inorganic->id; case Creature: From f6b6d730a08802deb9733e0119b5b8b24097bd0f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 7 Nov 2012 22:49:40 +0400 Subject: [PATCH 52/79] Fix fix-armory constantly trying to store ammo already stored in chest. --- plugins/fix-armory.cpp | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index ade9e4252..b937d40e8 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -160,7 +160,7 @@ static bool is_assigned_item(df::item *item) } // Check if this ammo item is assigned to this squad with one of the specified uses -static bool is_squad_ammo(df::item *item, df::squad *squad, bool train, bool combat) +static bool is_squad_ammo(df::item *item, df::squad *squad, bool combat, bool train) { for (size_t i = 0; i < squad->ammunition.size(); i++) { @@ -186,8 +186,6 @@ static bool can_store_ammo_rec(df::item *item, df::building *holder, int squad_i if (squads) { - bool target = holder->getType() == building_type::ArcheryTarget; - for (size_t i = 0; i < squads->size(); i++) { auto use = (*squads)[i]; @@ -198,8 +196,7 @@ static bool can_store_ammo_rec(df::item *item, df::building *holder, int squad_i // Squad Equipment -> combat bool combat = use->mode.bits.squad_eq; - // Archery target with Train -> training - bool train = target && use->mode.bits.train; + bool train = false; if (combat || train) { @@ -210,6 +207,41 @@ static bool can_store_ammo_rec(df::item *item, df::building *holder, int squad_i } } } + // Ugh, archery targets don't actually have a squad use vector + else if (holder->getType() == building_type::ArcheryTarget) + { + auto &squads = df::global::world->squads.all; + + for (size_t si = 0; si < squads.size(); si++) + { + auto squad = squads[si]; + + // For containers assigned to a squad, only consider that squad + if (squad_id >= 0 && squad->id != squad_id) + continue; + + for (size_t j = 0; j < squad->rooms.size(); j++) + { + auto use = squad->rooms[j]; + + if (use->building_id != holder->id) + continue; + + // Squad Equipment -> combat + bool combat = use->mode.bits.squad_eq; + // Archery target with Train -> training + bool train = use->mode.bits.train; + + if (combat || train) + { + if (is_squad_ammo(item, squad, combat, train)) + return true; + } + + break; + } + } + } for (size_t i = 0; i < holder->parents.size(); i++) if (can_store_ammo_rec(item, holder->parents[i], squad_id)) From 0c70a448d0f88823dd6ac236e8d123fa79da966f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 7 Nov 2012 23:06:02 +0100 Subject: [PATCH 53/79] Update submodules --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index fcacacce7..4ab899319 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit fcacacce7cf09cf70f011fea87b5be416da73457 +Subproject commit 4ab899319014d950214714a48cd3049a4beb5eb5 diff --git a/plugins/stonesense b/plugins/stonesense index 75df76626..cb97cf308 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 75df766263b23182820a1e07b330e64f87d5c9b7 +Subproject commit cb97cf308c6e09638c0de94894473c9bd0f561fd From a7bf526f41216f6e43c942c21907dbd46fe8f44f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 8 Nov 2012 21:27:56 +0400 Subject: [PATCH 54/79] Make workflow consider squad-assigned items busy. --- NEWS | 1 + plugins/workflow.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f2237cab9..32181f16a 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ DFHack future - properly considers minecarts assigned to routes busy. - code for deducing job outputs rewritten in lua for flexibility. - logic fix: collecting webs produces silk, and ungathered webs are not thread. + - items assigned to squads are considered busy, even if not in inventory. New Fix Armory plugin: Together with a couple of binary patches and the gui/assign-rack script, this plugin makes weapon racks, armor stands, chests and cabinets in diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index d46daed44..5347d4671 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1004,6 +1004,12 @@ static bool isRouteVehicle(df::item *item) return vehicle && vehicle->route_id >= 0; } +static bool isAssignedSquad(df::item *item) +{ + auto &vec = ui->equipment.items_assigned[item->getType()]; + return binsearch_index(vec, &df::item::id, item->id) >= 0; +} + static void map_job_items(color_ostream &out) { for (size_t i = 0; i < constraints.size(); i++) @@ -1117,8 +1123,10 @@ static void map_job_items(color_ostream &out) item->isAssignedToStockpile() || isRouteVehicle(item) || itemInRealJob(item) || - itemBusy(item)) + itemBusy(item) || + isAssignedSquad(item)) { + is_invalid = true; cv->item_inuse++; } else From eb936c4ce07bb16684de0157a2ab5bbdf74fc4ca Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 10 Nov 2012 17:06:54 +0400 Subject: [PATCH 55/79] Support milking and shearing in workflow. --- NEWS | 1 + library/modules/Job.cpp | 2 +- library/modules/Materials.cpp | 2 ++ library/xml | 2 +- plugins/lua/workflow.lua | 23 +++++++++++++++++------ plugins/workflow.cpp | 4 +++- scripts/gui/workflow.lua | 2 +- 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 32181f16a..51321be95 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ DFHack future - code for deducing job outputs rewritten in lua for flexibility. - logic fix: collecting webs produces silk, and ungathered webs are not thread. - items assigned to squads are considered busy, even if not in inventory. + - shearing and milking jobs are supported, but only with generic MILK or YARN outputs. New Fix Armory plugin: Together with a couple of binary patches and the gui/assign-rack script, this plugin makes weapon racks, armor stands, chests and cabinets in diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index def3b4192..757000885 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -75,7 +75,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job) { df::general_ref *ref = pnew->references[i]; - if (virtual_cast(ref)) + if (virtual_cast(ref)) vector_erase_at(pnew->references, i); else pnew->references[i] = ref->clone(); diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 7c06aeb4c..4da484ade 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -425,6 +425,8 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat) TEST(glass, IS_GLASS); if (cat.bits.clay && linear_index(material->reaction_product.id, std::string("FIRED_MAT")) >= 0) return true; + if (cat.bits.milk && linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0) + return true; return false; } diff --git a/library/xml b/library/xml index 4ab899319..02e0e0d7b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4ab899319014d950214714a48cd3049a4beb5eb5 +Subproject commit 02e0e0d7b9a7ef708a621ef5511a24bf8657b4a2 diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index 4c011b24c..c3dbe20d9 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -301,15 +301,26 @@ function listWeakenedConstraints(outputs) local mask = cons.mat_mask if (cons.mat_type or -1) >= 0 then cons.mat_mask = nil + local info = dfhack.matinfo.decode(cons) + if info then + for i,flag in ipairs(df.dfhack_material_category) do + if flag and flag ~= 'wood2' and info:matches{[flag]=true} then + mask = mask or {} + mask[flag] = true + end + end + end end register(cons) if mask then - table.insert(generic, { - item_type = cons.item_type, - item_subtype = cons.item_subtype, - is_craft = cons.is_craft, - mat_mask = mask - }) + for k,v in pairs(mask) do + table.insert(generic, { + item_type = cons.item_type, + item_subtype = cons.item_subtype, + is_craft = cons.is_craft, + mat_mask = { [k] = v } + }) + end end table.insert(anymat, { item_type = cons.item_type, diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 5347d4671..c89d87333 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -377,7 +377,9 @@ static bool isSupportedJob(df::job *job) Job::getHolder(job) && (!job->job_items.empty() || job->job_type == job_type::CollectClay || - job->job_type == job_type::CollectSand); + job->job_type == job_type::CollectSand || + job->job_type == job_type::MilkCreature || + job->job_type == job_type::ShearCreature); } static bool isOptionEnabled(unsigned flag) diff --git a/scripts/gui/workflow.lua b/scripts/gui/workflow.lua index 8dc958062..366e3ec91 100644 --- a/scripts/gui/workflow.lua +++ b/scripts/gui/workflow.lua @@ -306,7 +306,7 @@ function JobConstraints:onNewConstraint() end dlg.showListPrompt( - 'Job Outputs', + 'New limit', 'Select one of the possible outputs:', COLOR_WHITE, choices, From 56ef33ea0e5c6236239b4af43f00cb182c181987 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 10 Nov 2012 17:33:05 +0400 Subject: [PATCH 56/79] Support building steam engines on top of brooks without any down stairs. --- library/include/TileTypes.h | 6 ++++++ library/xml | 2 +- plugins/steam-engine.cpp | 8 +++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/include/TileTypes.h b/library/include/TileTypes.h index d21fb3c17..48c10146a 100644 --- a/library/include/TileTypes.h +++ b/library/include/TileTypes.h @@ -203,6 +203,12 @@ namespace DFHack return ENUM_ATTR(tiletype_shape, passable_flow, tileShape(tiletype)); } + inline + bool FlowPassableDown(df::tiletype tiletype) + { + return ENUM_ATTR(tiletype_shape, passable_flow_down, tileShape(tiletype)); + } + inline bool isWalkable(df::tiletype tiletype) { diff --git a/library/xml b/library/xml index 02e0e0d7b..4b2124957 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 02e0e0d7b9a7ef708a621ef5511a24bf8657b4a2 +Subproject commit 4b2124957e282683480eaf05922e63c353364ec1 diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index d884191e5..60f38ef83 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -320,7 +320,7 @@ struct workshop_hook : df::building_workshopst { for (int y = y1; y <= y2; y++) { auto ptile = Maps::getTileType(x,y,z); - if (!ptile || !LowPassable(*ptile)) + if (!ptile || !FlowPassableDown(*ptile)) continue; auto pltile = Maps::getTileType(x,y,z-1); @@ -891,7 +891,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_hook, feed); * Scan raws for matching workshop buildings. */ -static bool find_engines() +static bool find_engines(color_ostream &out) { engines.clear(); @@ -943,6 +943,8 @@ static bool find_engines() if (!ws.gear_tiles.empty()) engines.push_back(ws); + else + out.printerr("%s has no gear tiles - ignoring.\n", wslist[i]->code.c_str()); } return !engines.empty(); @@ -973,7 +975,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan { switch (event) { case SC_WORLD_LOADED: - if (find_engines()) + if (find_engines(out)) { out.print("Detected steam engine workshops - enabling plugin.\n"); enable_hooks(true); From f86371cfc3114dc08963cb3d0a521ca3660ac5ad Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 10 Nov 2012 18:06:41 +0400 Subject: [PATCH 57/79] Try blocking any use of stockpiles for squad stuff in fix-armory. --- plugins/fix-armory.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index b937d40e8..99e5fd500 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -132,6 +132,9 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) * grace period during which the items can be instantly picked up again. */ +// Completely block the use of stockpiles +#define NO_STOCKPILES + // Check if the item is assigned to any use controlled by the military tab static bool is_assigned_item(df::item *item) { @@ -143,19 +146,6 @@ static bool is_assigned_item(df::item *item) if (idx < 0) return false; - // Exclude weapons used by miners, wood cutters etc - switch (type) { - case item_type::WEAPON: - // the game code also checks this for ammo, funnily enough - // maybe it's not just for weapons?.. - if (binsearch_index(ui->equipment.work_weapons, item->id) >= 0) - return false; - break; - - default: - break; - } - return true; } @@ -328,6 +318,16 @@ template struct armory_hook : Item { */ DEFINE_VMETHOD_INTERPOSE(bool, isCollected, ()) { +#ifdef NO_STOCKPILES + /* + * Completely block any items assigned to a squad from being stored + * in stockpiles. The reason is that I still observe haulers running + * around with bins to pick them up for some reason. There could be + * some unaccounted race conditions involved. + */ + if (is_assigned_item(this)) + return false; +#else // Block stockpiling of items in the armory. if (is_in_armory(this)) return false; @@ -354,6 +354,7 @@ template struct armory_hook : Item { return false; } } +#endif // Call the original vmethod return INTERPOSE_NEXT(isCollected)(); From f1d4eac70016ced41cb5e383e65763187a84b191 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 Nov 2012 11:58:43 +0200 Subject: [PATCH 58/79] Pre-class remove --- .../luafiles/friendship/friendship.asm | 10 +- .../Dfusion/luafiles/friendship/friendship.o | Bin 722 -> 854 bytes plugins/lua/dfusion/embark.lua | 2 + plugins/lua/dfusion/friendship.lua | 111 ++++++++++++++++++ plugins/lua/dfusion/friendship.o | Bin 0 -> 854 bytes plugins/lua/dfusion/srcs/compile.bat | 1 + plugins/lua/dfusion/srcs/embark.asm | 7 ++ plugins/lua/dfusion/srcs/friendship.asm | 106 +++++++++++++++++ 8 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 plugins/lua/dfusion/friendship.lua create mode 100644 plugins/lua/dfusion/friendship.o create mode 100644 plugins/lua/dfusion/srcs/compile.bat create mode 100644 plugins/lua/dfusion/srcs/embark.asm create mode 100644 plugins/lua/dfusion/srcs/friendship.asm diff --git a/plugins/Dfusion/luafiles/friendship/friendship.asm b/plugins/Dfusion/luafiles/friendship/friendship.asm index e56fe7f49..b649e38de 100644 --- a/plugins/Dfusion/luafiles/friendship/friendship.asm +++ b/plugins/Dfusion/luafiles/friendship/friendship.asm @@ -66,8 +66,10 @@ mov eax, [edi+0x8c] #jmp compare compare: push ecx +mark_racepointer: mov ebx,0xDEADBEEF #write a pointer to the list of allowed races -mov ecx,2000 #write a number of allowed races +mark_racecount: +mov ecx,0xDEADBEEF #write a number of allowed races loop1: cmp word[ebx+ecx*2],ax jz endok @@ -92,11 +94,13 @@ endfinal: pop ebx pop eax -mov [0xFEEDBEEF],eax #write a pointer to safe location (usually after this) +mark_safeloc1: +mov [0xDEADBEEF],eax #write a pointer to safe location (usually after this) pop eax pushfd inc eax #skip one instruction popfd push eax -mov eax,[0xFEEDBEEF] #write a pointer to safe location (same as above) +mark_safeloc2: +mov eax,[0xDEADBEEF] #write a pointer to safe location (same as above) ret diff --git a/plugins/Dfusion/luafiles/friendship/friendship.o b/plugins/Dfusion/luafiles/friendship/friendship.o index f956de3e0a6fce74f63feb2c41a1d07b7a441ec4..c801562dbc1bc9ab11c74929ef422aff7629dcb5 100644 GIT binary patch delta 177 zcmcb_dX0_Ohmn~91VR`Y7$i6H3Ny02-?#SOWEDnbp0)QP<~Ym^SO^vfX1pm4QpWMtiq_w^Y&lF9EZ693*Ya1`)_hEMAX_RACES then + error("race count must be less then "..MAX_RACES) + end + local rarr=self:allocate("race_array",'uint16_t',MAX_RACES) + local _,rarr_offset=df.sizeof(rarr) + self:set_marker_dword("racepointer",rarr_offset) + self:set_races(rarr) + self:set_marker_dword("racecount",#self.race_data) + local safe_loc=self:allocate("safe_loc",'uint32_t',1) + local _1,safe_loc_offset=df.sizeof(safe_loc) + self:set_marker_dword("safeloc1",safe_loc_offset) + self:set_marker_dword("safeloc2",safe_loc_offset) + local addr=self:move_to_df() + self:patchCalls(addr) + self.installed=true + end +return _ENV \ No newline at end of file diff --git a/plugins/lua/dfusion/friendship.o b/plugins/lua/dfusion/friendship.o new file mode 100644 index 0000000000000000000000000000000000000000..c801562dbc1bc9ab11c74929ef422aff7629dcb5 GIT binary patch literal 854 zcmeZaWM%+?5JmI3a0tG+rTticRPp<-2Hyv+Iu@eM4F|GH&Y3hXYaMV#5$~(mYI_Z_29q%3=HXvX^Gh|0jPLZZb3;>4g&)x zkPi%c1~!JG)O2T%WIVEn3rHj%S;Pq>l8z!00OXY;i?{}RGBDJmh`0bn+L1*ZfgFZl zxJX)JCeSqu4C~<{$@#ejiAAXly>JnbJPQ!_p@@hAd6VHHIr;eohCtp7xJYVVN`5ww zw-Q+-Ei*4MXB%7u$Y4+b;vFa=MnK*nxCnBp1cewgBPd;>0^}6Q1e9V1DMSOvsS%X+ uv8izYisvR4WycpKCZ`tUXXcfp79j+Z^GoweAl%}_wA7sZWJ4655d#3LE~Ebd literal 0 HcmV?d00001 diff --git a/plugins/lua/dfusion/srcs/compile.bat b/plugins/lua/dfusion/srcs/compile.bat new file mode 100644 index 000000000..e084949f4 --- /dev/null +++ b/plugins/lua/dfusion/srcs/compile.bat @@ -0,0 +1 @@ +as -anl --32 -o friendship.o friendship.asm \ No newline at end of file diff --git a/plugins/lua/dfusion/srcs/embark.asm b/plugins/lua/dfusion/srcs/embark.asm new file mode 100644 index 000000000..d2fa91081 --- /dev/null +++ b/plugins/lua/dfusion/srcs/embark.asm @@ -0,0 +1,7 @@ +.intel_syntax +mov eax , [esp+0x1C] # loop counter +mark_caste: +movsx ecx, word ptr[eax*2+0xdeadbeef] +mark_race: +movzx eax,word ptr [eax*2+0xDEADBEEF] +ret diff --git a/plugins/lua/dfusion/srcs/friendship.asm b/plugins/lua/dfusion/srcs/friendship.asm new file mode 100644 index 000000000..b649e38de --- /dev/null +++ b/plugins/lua/dfusion/srcs/friendship.asm @@ -0,0 +1,106 @@ +.intel_syntax +push eax +mov eax,[esp+0x04] +push ebx +pushfd +mov eax,[eax] # get a byte after the call this procedure to analyze what register holds cr ptr +jmptbl: +cmp al,0x81 +jz regC +cmp al,0x82 +jz regD +cmp al,0x83 +jz regB +cmp al,0x85 +jz regBP +cmp al,0x86 +jz regESI +cmp al,0x87 +jz regEDI +cmp al,0x88 +jz regA +cmp al,0x8A +jz regD +cmp al,0x8B +jz regB +cmp al,0x8D +jz regBP +cmp al,0x8E +jz regESI +cmp al,0x8F +jz regEDI +cmp al,0x90 +jz regA +cmp al,0x91 +jz regC +cmp al,0x93 +jz regB +cmp al,0x95 +jz regBP +cmp al,0x96 +jz regESI +cmp al,0x97 +jz regEDI +jmp fail +regA: +mov eax, [esp+0x8] +mov eax, [eax+0x8c] +jmp compare +regC: +mov eax, [ecx+0x8c] +jmp compare +regB: +mov eax, [ebx+0x8c] +jmp compare +regD: +mov eax, [edx+0x8c] +jmp compare +regBP: +mov eax, [ebp+0x8c] +jmp compare +regESI: +mov eax, [esi+0x8c] +jmp compare +regEDI: +mov eax, [edi+0x8c] +#jmp compare +compare: +push ecx +mark_racepointer: +mov ebx,0xDEADBEEF #write a pointer to the list of allowed races +mark_racecount: +mov ecx,0xDEADBEEF #write a number of allowed races +loop1: +cmp word[ebx+ecx*2],ax +jz endok +dec ecx +cmp ecx ,-1 +jnz loop1 +pop ecx +popfd +jmp fail +endok: +pop ecx +popfd +cmp eax,eax +jmp endfinal +fail: + +xor ebx,ebx +xor eax,eax +inc eax +cmp eax,ebx +endfinal: + +pop ebx +pop eax +mark_safeloc1: +mov [0xDEADBEEF],eax #write a pointer to safe location (usually after this) +pop eax +pushfd +inc eax #skip one instruction +popfd +push eax +mark_safeloc2: +mov eax,[0xDEADBEEF] #write a pointer to safe location (same as above) +ret From 33f674eee2ce40b5528b5ef6b4f4681867299829 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 Nov 2012 12:33:54 +0200 Subject: [PATCH 59/79] Removed dfusion lua files. Updated plugins. --- plugins/Dfusion/CMakeLists.txt | 3 - plugins/Dfusion/luafiles/adv_tools/plugin.lua | 3 - plugins/Dfusion/luafiles/common.lua | 514 ------------------ plugins/Dfusion/luafiles/embark/build.bat | 1 - plugins/Dfusion/luafiles/embark/embark.asm | 7 - plugins/Dfusion/luafiles/embark/embark.o | Bin 369 -> 0 bytes plugins/Dfusion/luafiles/embark/init.lua | 95 ---- plugins/Dfusion/luafiles/embark/plugin.lua | 5 - plugins/Dfusion/luafiles/embark/races.txt | 9 - .../Dfusion/luafiles/friendship/compile.bat | 1 - .../luafiles/friendship/friendship.asm | 106 ---- .../Dfusion/luafiles/friendship/friendship.o | Bin 854 -> 0 bytes plugins/Dfusion/luafiles/friendship/init.lua | 45 -- .../Dfusion/luafiles/friendship/install.lua | 35 -- plugins/Dfusion/luafiles/friendship/patch.lua | 57 -- .../Dfusion/luafiles/friendship/plugin.lua | 18 - plugins/Dfusion/luafiles/friendship/races.txt | 8 - .../luafiles/friendship_civ/compile.bat | 1 - .../luafiles/friendship_civ/friendship_c.asm | 41 -- .../luafiles/friendship_civ/friendship_c.o | Bin 462 -> 0 bytes .../Dfusion/luafiles/friendship_civ/init.lua | 89 --- .../luafiles/friendship_civ/plugin.lua | 57 -- plugins/Dfusion/luafiles/init.lua | 92 ---- plugins/Dfusion/luafiles/migrants/compile.bat | 1 - plugins/Dfusion/luafiles/migrants/init.lua | 62 --- .../Dfusion/luafiles/migrants/migrants.asm | 20 - plugins/Dfusion/luafiles/migrants/migrants.o | Bin 336 -> 0 bytes plugins/Dfusion/luafiles/migrants/plugin.lua | 5 - plugins/Dfusion/luafiles/migrants/races.txt | 29 - plugins/Dfusion/luafiles/offsets/plugin.lua | 2 - plugins/Dfusion/luafiles/offsets_misc.lua | 48 -- .../Dfusion/luafiles/simple_embark/plugin.lua | 23 - plugins/Dfusion/luafiles/tools/init.lua | 511 ----------------- plugins/Dfusion/luafiles/tools/plugin.lua | 8 - plugins/Dfusion/luafiles/utils.lua | 1 - plugins/lua/dfusion/embark.lua | 92 ++-- plugins/lua/dfusion/friendship.lua | 5 +- 37 files changed, 36 insertions(+), 1958 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/adv_tools/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/common.lua delete mode 100644 plugins/Dfusion/luafiles/embark/build.bat delete mode 100644 plugins/Dfusion/luafiles/embark/embark.asm delete mode 100644 plugins/Dfusion/luafiles/embark/embark.o delete mode 100644 plugins/Dfusion/luafiles/embark/init.lua delete mode 100644 plugins/Dfusion/luafiles/embark/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/embark/races.txt delete mode 100644 plugins/Dfusion/luafiles/friendship/compile.bat delete mode 100644 plugins/Dfusion/luafiles/friendship/friendship.asm delete mode 100644 plugins/Dfusion/luafiles/friendship/friendship.o delete mode 100644 plugins/Dfusion/luafiles/friendship/init.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/install.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/patch.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/races.txt delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/compile.bat delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/friendship_c.asm delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/friendship_c.o delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/init.lua delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/init.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/compile.bat delete mode 100644 plugins/Dfusion/luafiles/migrants/init.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/migrants.asm delete mode 100644 plugins/Dfusion/luafiles/migrants/migrants.o delete mode 100644 plugins/Dfusion/luafiles/migrants/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/races.txt delete mode 100644 plugins/Dfusion/luafiles/offsets/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/offsets_misc.lua delete mode 100644 plugins/Dfusion/luafiles/simple_embark/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/tools/init.lua delete mode 100644 plugins/Dfusion/luafiles/tools/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/utils.lua diff --git a/plugins/Dfusion/CMakeLists.txt b/plugins/Dfusion/CMakeLists.txt index 51f2e3bee..6cfe9eb40 100644 --- a/plugins/Dfusion/CMakeLists.txt +++ b/plugins/Dfusion/CMakeLists.txt @@ -9,6 +9,3 @@ set( FILE(GLOB DFUSION_HS include/*) SET_SOURCE_FILES_PROPERTIES( ${DFUSION_HS} PROPERTIES HEADER_FILE_ONLY TRUE ) DFHACK_PLUGIN(dfusion ${DFUSION_CPPS_ALL} ${DFUSION_HS} LINK_LIBRARIES lua dfhack-tinythread) - -# installs into DF root -install(DIRECTORY luafiles/ DESTINATION dfusion) \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/adv_tools/plugin.lua b/plugins/Dfusion/luafiles/adv_tools/plugin.lua deleted file mode 100644 index cdd81e06f..000000000 --- a/plugins/Dfusion/luafiles/adv_tools/plugin.lua +++ /dev/null @@ -1,3 +0,0 @@ -if not(FILE) then - adv_tools.menu:display() -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua deleted file mode 100644 index bdf3a2bc8..000000000 --- a/plugins/Dfusion/luafiles/common.lua +++ /dev/null @@ -1,514 +0,0 @@ -dofile("dfusion/offsets_misc.lua") -STD_STRING=0 -DWORD=1 -WORD=2 -BYTE=3 -QWORD=4 -DOUBLE=5 -FLOAT=6 - -getline=function (inp) -return dfhack.lineedit(inp or "") -end -io.stdin=nil - -function printd(...) - if DEBUG then - print(...) - end -end -function GetTextRegion() - if __TEXT ~=nil then --Chache this, not going to change. - return __TEXT - end - - ranges__=Process.getMemRanges() - --print("Ranges:"..#ranges__) - for k,v in pairs(ranges__) do - for k2,v2 in pairs(v) do - --print(string.format("%d %s->%s",k,tostring(k2),tostring(v2))) - end - --local num - --flgs="" - --if(v["read"])then flgs=flgs..'r' end - --if(v["write"])then flgs=flgs..'w' end - --if(v["execute"]) then flgs=flgs..'e' end - --if num>=100 then - --print(string.format("%d %x->%x %s %s",k,v["start"],v["end"],v.name or "",flgs)) - --end - local pos=string.find(v.name,"Dwarf Fortress.exe") or string.find(v.name,"libs/Dwarf_Fortress") - if(pos~=nil) and v["execute"] then - __TEXT=v; - return v; - end - end - error(".Text region not found!") -end -function UpdateRanges() - ranges__=Process.getMemRanges() -end -function GetRegionIn(pos) - ranges__=ranges__ or Process.getMemRanges() - for k,v in pairs(ranges__) do - if pos>=v.start and pos%s",k,tostring(k2),tostring(v2))) - --end - --local num - --num=0 - --if(v["read"])then num=num+1 end - --if(v["write"])then num=num+10 end - --if(v["execute"]) then num=num+100 end - --print(string.format("%d %x->%x %s %x",k,v["start"],v["end"],v.name,pos)) - if pos>=v.start then --This is a hack to counter .text region suddenly shrinking. - if cr~=nil then - if v.start < cr.start then -- find region that start is closest - cr=v - end - else - cr=v - end - end - end - return cr -end -function ValidOffset(pos) - ranges__=ranges__ or Process.getMemRanges() - return GetRegionIn(pos)~=nil -end -function unlockDF() - local reg=GetTextRegion() - reg["write"]=true - Process.setPermisions(reg,reg) -end -function lockDF() - local reg=GetTextRegion() - reg["write"]=false - Process.setPermisions(reg,reg) -end -function SetExecute(pos) - UpdateRanges() - local reg=GetRegionIn(pos) - reg.execute=true - reg["write"]=true - Process.setPermisions(reg,reg) -- TODO maybe make a page with only execute permisions or sth -end --- engine bindings -engine=engine or {} ---[=[ use default peek/pokes for now -engine.peekd=Process.readDWord -engine.poked=Process.writeDWord -engine.peekb=Process.readByte -engine.pokeb=Process.writeByte -engine.peekw=Process.readWord -engine.pokew=Process.writeWord -engine.peekstr_stl=Process.readSTLString -engine.pokestr_stl=Process.writeSTLString -engine.peekstr=Process.readCString ---engine.pokestr=Process.readCString -engine.peekarb=Process.read -engine.pokearb=Process.write ---]=] - -function engine.peek(offset,rtype) - if type(rtype)=="table" then - if rtype.off ==nil then - return engine.peekpattern(offset,rtype) - else - return engine.peek(rtype.off+offset,rtype.rtype) - end - end - if rtype==STD_STRING then - return engine.peekstr2(offset) - elseif rtype==DWORD then - return engine.peekd(offset) - elseif rtype==WORD then - return engine.peekw(offset) - elseif rtype==BYTE then - return engine.peekb(offset) - elseif rtype==QWORD then - return engine.peekq(offset) - elseif rtype==FLOAT then - return engine.peekfloat(offset) - elseif rtype==DOUBLE then - return engine.peekdouble(offset) - else - error("Invalid peek type") - return - end -end -function engine.poke(offset,rtype,val) - if type(rtype)=="table" then - if rtype.off ==nil then - return engine.pokepattern(offset,rtype,val) - else - return engine.poke(rtype.off+offset,rtype.rtype,val) - end - end - if rtype==STD_STRING then - return engine.pokestr2(offset,val) - elseif rtype==DWORD then - return engine.poked(offset,val) - elseif rtype==WORD then - return engine.pokew(offset,val) - elseif rtype==BYTE then - return engine.pokeb(offset,val) - elseif rtype==QWORD then - return engine.pokeq(offset,val) - elseif rtype==FLOAT then - return engine.pokefloat(offset,val) - elseif rtype==DOUBLE then - return engine.pokedouble(offset,val) - else - error("Invalid poke type:"..tostring(rtype)) - return - end -end -function engine.sizeof(rtype) - if rtype==STD_STRING then - error("String has no constant size") - return - elseif rtype==DWORD then - return 4; - elseif rtype==WORD then - return 2; - elseif rtype==BYTE then - return 1; - else - error("Invalid sizeof type") - return - end -end -function engine.peekpattern(offset,pattern) - local ret={} - for k,v in pairs(pattern) do - --print("k:"..k.." v:"..type(v)) - if type(v)=="table" then - ret[k]=engine.peek(offset+v.off,v.rtype) - --print(k.." peeked:"..offset+v.off) - else - ret[k]=v - end - end - ret.__offset=offset - return ret -end -function engine.pokepattern(offset,pattern,val) - for k,v in pairs(pattern) do - --print("k:"..k.." v:"..type(v)) - if type(v)=="table" then - engine.poke(offset+v.off,v.rtype,val[k]) - end - end -end - -function engine.LoadModData(file) - local T2={} - T2.symbols={} - T2.data,T2.size=engine.loadobj(file) - data,modsize=engine.loadobj(file) - local T=engine.loadobjsymbols(file) - for k,v in pairs(T) do - - if v.pos~=0 then - T2.symbols[v.name]=v.pos - end - end - return T2 -end -function engine.FindMarkerCall(moddata,name) - if moddata.symbols[name] ~=nil then - return moddata.symbols[name]+1 - end -end -function engine.FindMarker(moddata,name) - if moddata.symbols[name] ~=nil then - return engine.findmarker(0xDEADBEEF,moddata.data,moddata.size,moddata.symbols[name]) - end -end -function engine.installMod(file,name,bonussize) - local T=engine.LoadModData(file) - local modpos,modsize=engine.loadmod(file,name,bonussize) - T.pos=modpos - return T -end - - - -function PrintPattern(loadedpattern) - for k,v in pairs(loadedpattern) do - if type(v)== "string" then - print(k.." "..v) - else - print(string.format("%s %d inhex:%x",k,v,v)) - end - end -end - -function printPattern(pattern) - local i=0; - local names={} - names[STD_STRING]="std_string (STD_STRING)" - names[DWORD]= "Double word (DWORD)" - names[WORD]= "Word (WORD)" - names[STD_STRING]="Byte (BYTE)" - ret={} - for k,v in pairs(pattern) do - if type(v)=="table" and v.off~=nil then - - if names[v.rtype]~=nil then - lname=names[v.rtype] - else - if type(v.rtype)=="table" then - lname="Table (prob subpattern)" - else - lname="Other" - end - end - print(string.format("%d. %s is %s with offset %x",i,k,lname,v.off)) - table.insert(ret,k) - else - print(string.format("%d. %s",i,k)) - end - i=i+1 - end - return ret; -end -function editPattern(offset,pattern,name) - if type(pattern[name].rtype)=="table" then - if pattern[name].rtype.setval~=nil then - print(string.format("%x",offset+pattern[name].off)) - local t=engine.peek(offset+pattern[name].off,pattern[name].rtype) - print("Value is now:"..t:getval()) - print("Enter new value:") - val=io.stdin:read() - t:setval(val) - else - ModPattern(offset+pattern[name].off,pattern[name].rtype) - end - return - end - val=engine.peek(offset,pattern[name]) - print("Value is now:"..val) - print("Enter new value:") - if pattern[name].rtype==STD_STRING then - val=io.stdin:read() - else - val=tonumber(io.stdin:read()) - end - engine.poke(offset,pattern[name],val) -end -function ModPattern(itemoffset,pattern) - print("Select what to edit:") - nm=printPattern(pattern) - q=tonumber(io.stdin:read()) - if q~=nil and q<#nm then - editPattern(itemoffset,pattern,nm[q+1]) - end -end - -function findVectors() - if __VECTORS ~=nil then --chache - return __VECTORS - end - local text=GetTextRegion() - local h=hexsearch(text.start,text["end"],0x8b,ANYBYTE,ANYDWORD,0x8b,ANYBYTE,ANYDWORD,0x2b) - local pos=h:findall() - local T={} - for k,v in pairs(pos) do - local loc1,loc2 - loc1=engine.peekd(v+2) - loc2=engine.peekd(v+8) - --print(string.format("%x - %x=%x",loc1,loc2,loc1-loc2)) - if(loc1-loc2==4) then - if T[loc1-4]~=nil then - T[loc1-4]=T[loc1-4]+1 - else - T[loc1-4]=1 - end - end - end - __VECTORS=T - return T -end - -function GetRaceToken(p) --actually gets token... - local vec=df.global.world.raws.creatures.all - return vec[p].creature_id -end -function BuildNameTable() - local rtbl={} - local vec=df.global.world.raws.creatures.all - --print(string.format("Vector start:%x",vec.st)) - --print(string.format("Vector end:%x",vec.en)) - --print("Creature count:"..vec.size) - for k=0,#vec-1 do - local name=vec[k].creature_id - --print(k.." "..tostring(name)) - rtbl[name]=k - end - return rtbl; -end -function BuildMaterialTable() - local rtbl={} - local vec=engine.peek(offsets.getEx('Materials'),ptr_vector) - --print(string.format("Vector start:%x",vec.st)) - --print(string.format("Vector end:%x",vec.en)) - --local i=0 - for p=0,vec:size()-1 do - local off=vec:getval(p) - --print("First member:"..off) - local name=engine.peek(off,ptt_dfstring) - --print("Loading:"..p.."="..name:getval()) - rtbl[name:getval()]=p - --i=i+1 - --if i>100 then - -- io.stdin:read() - -- i=0 - --end - end - return rtbl; -end -function BuildWordTables() - local names={} - local rnames={} - local vector=engine.peek(offsets.getEx('WordVec'),ptr_vector) -for i =0,vector:size()-1 do - local off=vector:getval(i) - local n=engine.peekstr(off) - names[i]=n - rnames[n]=i -end - return names,rnames -end -function ParseScript(file) - - io.input(file) - f="" - first=0 - nobraces=0 - function updFunction() - if f~="" then - first=0 - if nobraces==0 then - f=f.."}" - end - nobraces=0 - print("Doing:"..f) - assert(loadstring(f))() - - f="" - end - end - while true do - - local line = io.read("*line") - if line == nil then break end - if string.sub(line,1,2)==">>" then - updFunction() - if string.find(line,"%b()") then - f=string.sub(line,3) - nobraces=1 - else - f=string.sub(line,3).."{" - end - --print(string.sub(line,3)..) - else - if first~=0 then - f=f.."," - else - first=1 - end - f=f..string.format('%q',line) - - end - - end - updFunction() -end -function ParseNames(path) - local ret={} - local ff=io.open(path) - for n in ff:lines() do - table.insert(ret,n) - end - return ret -end -function getSelectedUnit() - if df.global.ui.main.mode~=23 then - return nil - end - local unit_indx=df.global.ui_selected_unit - if unit_indx<#df.global.world.units.active-1 then - return df.global.world.units.active[unit_indx] - else - return nil - end -end -function getxyz() -- this will return pointers x,y and z coordinates. - local x=df.global.cursor.x - local y=df.global.cursor.y - local z=df.global.cursor.z - return x,y,z -- return the coords -end -function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord - --local x,y,z=getxyz() --get 'X' coords - local vector=df.global.world.units.all -- load all creatures - for i = 0, #vector-1 do -- look into all creatures offsets - local curpos=vector[i].pos --get its coordinates - local cx=curpos.x - local cy=curpos.y - local cz=curpos.z - if cx==x and cy==y and cz==z then --compare them - return vector[i] --return index - end - end - --print("Creature not found!") - return nil - -end -function getCreatureAtPointer() - return getCreatureAtPos(getxyz()) -end -function getCreature() - local unit=getSelectedUnit() - if unit==nil then - unit=getCreatureAtPointer() - end - --any other selection methods... - return unit -end -function getNemesisId(unit) - for k,v in pairs(unit.refs) do - if df.general_ref_is_nemesisst:is_instance(v) then - return v.nemesis_id - end - end -end -function getNemesis(unit) - local id=getNemesisId(unit) - if id then - return df.nemesis_record.find(id) - end -end -function Allocate(size) - local ptr=engine.getmod('General_Space') - if ptr==nil then - ptr=engine.newmod("General_Space",4096) -- some time later maybe make some more space - engine.poked(ptr,4) - end - - local curptr=engine.peekd(ptr) - curptr=curptr+size - engine.poked(ptr,curptr) - return curptr-size+ptr -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/embark/build.bat b/plugins/Dfusion/luafiles/embark/build.bat deleted file mode 100644 index 51eeb8140..000000000 --- a/plugins/Dfusion/luafiles/embark/build.bat +++ /dev/null @@ -1 +0,0 @@ -as -a --32 -o embark.o embark.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/embark/embark.asm b/plugins/Dfusion/luafiles/embark/embark.asm deleted file mode 100644 index d2fa91081..000000000 --- a/plugins/Dfusion/luafiles/embark/embark.asm +++ /dev/null @@ -1,7 +0,0 @@ -.intel_syntax -mov eax , [esp+0x1C] # loop counter -mark_caste: -movsx ecx, word ptr[eax*2+0xdeadbeef] -mark_race: -movzx eax,word ptr [eax*2+0xDEADBEEF] -ret diff --git a/plugins/Dfusion/luafiles/embark/embark.o b/plugins/Dfusion/luafiles/embark/embark.o deleted file mode 100644 index 87f5bbd68f5c8d0aaf6f81485f653a440145a952..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 369 zcmeZaWM%+?B|yvtX0bBrm84dbfY}g20!Z~B@j*-l27?5>l*E!mG;wsU1B1ZMAX_RACES then - error("caste and race count must be less then "..MAX_RACES) - end - if stoff==nil then - error("address for start_dwarf_count not found!") - end - local _,race_id_offset=df.sizeof(df.global.ui:_field("race_id")) - print(string.format("start=%08x",stoff)) - local needle={0x0f,0xb7,0x0d} --movzx eax,dword ptr [race_id] - local tmp_table=dfu.dwordToTable(race_id_offset) - for k,v in ipairs(tmp_table) do - table.insert(needle,v) - end - - local mem=ms.get_code_segment() - print(mem.uint8_t:addr2idx(stoff)) - print(mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff))) - local _,trg_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(stoff),nil)--maybe endoff=stoff+bignumber - if trg_offset==nil then - error("address for race_load not found") - end - local call_data={0x90,0x90} - local _,data_offset=df.sizeof(self.data) - dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) - self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} - needle={0x83,0xc8,0xff} -- or eax, 0xFF - local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) - if caste_offset==nil or caste_offset-stoff>1000 then - error("Caste change code not found or found too far!") - end - - self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} - self.disable_castes:apply() - self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} - self.dwarfcount:apply() - local caste_array=self:allocate("caste_array","uint16_t",#self.race_caste_data) - local race_array=self:allocate("race_array","uint16_t",#self.race_caste_data) - self:setEmbarkParty(self.race_caste_data) - for k,v in ipairs(self.race_caste_data) do - caste_array[k-1]=v[2] - race_array[k-1]=v[1] - end - local race_array_off,caste_array_off - local _ - _,race_array_off=df.sizeof(race_array) - _,caste_array_off=df.sizeof(caste_array) - self:set_marker_dword("race",caste_array_off) --hehe... mixed them up i guess... - self:set_marker_dword("caste",race_array_off) - - self:move_to_df() - self.call_patch:apply() - self.installed=true - end - function CustomEmbark:setEmbarkParty(racesAndCastes) - self.race_caste_data=racesAndCastes - if self.dwarfcount== nil then - self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} - self.dwarfcount:apply() - else - self.dwarfcount:repatch(dfu.dwordToTable(#self.race_caste_data)) - end - - end - function CustomEmbark:status() - if self.installed then - return "valid, installed" - else - return "valid, not installed" - end - end - function CustomEmbark:uninstall() - if self.installed then - self.call_patch:remove() - self.disable_castes:remove() - self.dwarfcount:remove() - end - end -else - CustomEmbark.class_status="invalid, os not supported" -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/embark/plugin.lua b/plugins/Dfusion/luafiles/embark/plugin.lua deleted file mode 100644 index 039000656..000000000 --- a/plugins/Dfusion/luafiles/embark/plugin.lua +++ /dev/null @@ -1,5 +0,0 @@ - -if not(FILE)then - names=ParseNames("dfusion/embark/races.txt")--io.open("plugins/embark/races.txt"):lines() - embark(names) -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/embark/races.txt b/plugins/Dfusion/luafiles/embark/races.txt deleted file mode 100644 index 376928866..000000000 --- a/plugins/Dfusion/luafiles/embark/races.txt +++ /dev/null @@ -1,9 +0,0 @@ -ANT_MAN:0 -ANT_MAN:0 -ANT_MAN:0 -ANT_MAN:1 -ANT_MAN:1 -ANT_MAN:0 -ANT_MAN:0 -ANT_MAN:2 -ANT_MAN:3 \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship/compile.bat b/plugins/Dfusion/luafiles/friendship/compile.bat deleted file mode 100644 index e084949f4..000000000 --- a/plugins/Dfusion/luafiles/friendship/compile.bat +++ /dev/null @@ -1 +0,0 @@ -as -anl --32 -o friendship.o friendship.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship/friendship.asm b/plugins/Dfusion/luafiles/friendship/friendship.asm deleted file mode 100644 index b649e38de..000000000 --- a/plugins/Dfusion/luafiles/friendship/friendship.asm +++ /dev/null @@ -1,106 +0,0 @@ -.intel_syntax -push eax -mov eax,[esp+0x04] -push ebx -pushfd -mov eax,[eax] # get a byte after the call this procedure to analyze what register holds cr ptr -jmptbl: -cmp al,0x81 -jz regC -cmp al,0x82 -jz regD -cmp al,0x83 -jz regB -cmp al,0x85 -jz regBP -cmp al,0x86 -jz regESI -cmp al,0x87 -jz regEDI -cmp al,0x88 -jz regA -cmp al,0x8A -jz regD -cmp al,0x8B -jz regB -cmp al,0x8D -jz regBP -cmp al,0x8E -jz regESI -cmp al,0x8F -jz regEDI -cmp al,0x90 -jz regA -cmp al,0x91 -jz regC -cmp al,0x93 -jz regB -cmp al,0x95 -jz regBP -cmp al,0x96 -jz regESI -cmp al,0x97 -jz regEDI -jmp fail -regA: -mov eax, [esp+0x8] -mov eax, [eax+0x8c] -jmp compare -regC: -mov eax, [ecx+0x8c] -jmp compare -regB: -mov eax, [ebx+0x8c] -jmp compare -regD: -mov eax, [edx+0x8c] -jmp compare -regBP: -mov eax, [ebp+0x8c] -jmp compare -regESI: -mov eax, [esi+0x8c] -jmp compare -regEDI: -mov eax, [edi+0x8c] -#jmp compare -compare: -push ecx -mark_racepointer: -mov ebx,0xDEADBEEF #write a pointer to the list of allowed races -mark_racecount: -mov ecx,0xDEADBEEF #write a number of allowed races -loop1: -cmp word[ebx+ecx*2],ax -jz endok -dec ecx -cmp ecx ,-1 -jnz loop1 -pop ecx -popfd -jmp fail -endok: -pop ecx -popfd -cmp eax,eax -jmp endfinal -fail: - -xor ebx,ebx -xor eax,eax -inc eax -cmp eax,ebx -endfinal: - -pop ebx -pop eax -mark_safeloc1: -mov [0xDEADBEEF],eax #write a pointer to safe location (usually after this) -pop eax -pushfd -inc eax #skip one instruction -popfd -push eax -mark_safeloc2: -mov eax,[0xDEADBEEF] #write a pointer to safe location (same as above) -ret diff --git a/plugins/Dfusion/luafiles/friendship/friendship.o b/plugins/Dfusion/luafiles/friendship/friendship.o deleted file mode 100644 index c801562dbc1bc9ab11c74929ef422aff7629dcb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 854 zcmeZaWM%+?5JmI3a0tG+rTticRPp<-2Hyv+Iu@eM4F|GH&Y3hXYaMV#5$~(mYI_Z_29q%3=HXvX^Gh|0jPLZZb3;>4g&)x zkPi%c1~!JG)O2T%WIVEn3rHj%S;Pq>l8z!00OXY;i?{}RGBDJmh`0bn+L1*ZfgFZl zxJX)JCeSqu4C~<{$@#ejiAAXly>JnbJPQ!_p@@hAd6VHHIr;eohCtp7xJYVVN`5ww zw-Q+-Ei*4MXB%7u$Y4+b;vFa=MnK*nxCnBp1cewgBPd;>0^}6Q1e9V1DMSOvsS%X+ uv8izYisvR4WycpKCZ`tUXXcfp79j+Z^GoweAl%}_wA7sZWJ4655d#3LE~Ebd diff --git a/plugins/Dfusion/luafiles/friendship/init.lua b/plugins/Dfusion/luafiles/friendship/init.lua deleted file mode 100644 index 0fc3219e4..000000000 --- a/plugins/Dfusion/luafiles/friendship/init.lua +++ /dev/null @@ -1,45 +0,0 @@ -function analyzeF(off) - pos=offsets.find(off,0x39,ANYBYTE,0x8c,00,00,00) - print(string.format("Compare at:%x",pos)) - if pos ==0 then - return 0 - end - if(pos-off>0x100) then - print(string.format("Distance to cmp:%x",pos-off)) - pos =offsets.find(off,CALL) - print(string.format("Distance to call:%x",pos-off)) - return 0 - --return analyzeF(pos) - else - return pos - end -end -function minEx(list) - local imin=list[1] - for _,v in ipairs(list) do - if imin> v and v~=0 then - imin=v - end - end - return imin -end -function signDword(dw) - if(dw>0xFFFFFFFF) then - return dw-0xFFFFFFFF - end - return dw -end ---[[ - Warning: not all mov's are acounted for. Found one: mov EAX,WORD PTR[EBP+1EF4] WTF?? - Two more compares are missing. There are calls instead (same function) -]]-- - -friendship_in={} -dofile("dfusion/friendship/install.lua") -dofile("dfusion/friendship/patch.lua") - -function friendship(names) - friendship_in.install(names) - friendship_in.patch() -end - diff --git a/plugins/Dfusion/luafiles/friendship/install.lua b/plugins/Dfusion/luafiles/friendship/install.lua deleted file mode 100644 index 251d2fb3a..000000000 --- a/plugins/Dfusion/luafiles/friendship/install.lua +++ /dev/null @@ -1,35 +0,0 @@ - -function friendship_in.install(names) -RaceTable=RaceTable or BuildNameTable() -mypos=engine.getmod("Friendship") -if mypos then - modpos=mypos - _,modsize=engine.loadobj("dfusion/friendship/friendship.o") - _=nil -else - modpos,modsize=engine.loadmod("dfusion/friendship/friendship.o","Friendship",1024) - print(string.format("Loaded module @:%x",modpos)) -end -count=0 -for _,v in pairs(names) do - if RaceTable[v] == nil then - --print("Failure, "..v.." not found!") - error("Failure, "..v.." not found!") - --break --maybe some indication of failure? and cleanup? - end - engine.pokew(modpos+modsize+count*2+4+2,RaceTable[v]) -- for some reason it compiled strangely - -- cmp word[ebx+ecx*2],ax -> cmp word[ebx+ecx*2+2],ax - count = count + 1 -end -engine.poked(modpos+0x8f,modpos+modsize+4) -- set ptr to creatures -engine.poked(modpos+0x94,count) -- set count of creatures -engine.poked(modpos+0xb9,modpos+modsize) -- set safe location -engine.poked(modpos+0xc3,modpos+modsize) -- set safe location -SetExecute(modpos) -end -function pokeCall(off) - engine.pokeb(off,0xe8) - b=engine.peekb(off+1) - engine.poked(off+1,modpos-off-5) - engine.pokeb(off+5,b) -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship/patch.lua b/plugins/Dfusion/luafiles/friendship/patch.lua deleted file mode 100644 index 247766db1..000000000 --- a/plugins/Dfusion/luafiles/friendship/patch.lua +++ /dev/null @@ -1,57 +0,0 @@ -function friendship_in.patch() - UpdateRanges() - pos=GetTextRegion().start - local _,crace=df.sizeof(df.global.ui:_field("race_id")) - hits={} - i=1 - repeat - --todo make something better/smarter... - pos1=offsets.find(pos+7,0x0f,0xBF,ANYBYTE,DWORD_,crace) -- movsx - pos2=offsets.find(pos+7,0x66,0xa1,DWORD_,crace) -- mov ax,[ptr] - pos3=offsets.find(pos+7,0xa1,DWORD_,crace) -- mov eax,[ptr] - pos4=offsets.find(pos+7,0x66,0x8b,ANYBYTE,DWORD_,crace) -- mov ANYREG,[ptr] - --pos5=offsets.find(pos+7,0x66,0x8b,0x15,DWORD_,crace) -- mov dx,[ptr] - pos=minEx{pos1,pos2,pos3,pos4} - if pos ~=0 then - hits[i]=pos - i=i+1 - print(string.format("Found at %x",pos)) - end - until pos==0 - print("=======================================") - for _,p in pairs(hits) do - myp=p - repeat - - --print(string.format("Analyzing %x...",p)) - --TODO read offset from memory.xml - pos1=offsets.find(myp,0x39,ANYBYTE,0x8c,00,00,00) -- compare [reg+08c] (creature race) with any reg - pos2=offsets.find(myp,0x3b,ANYBYTE,0x8c,00,00,00) -- compare any reg with [reg+08c] (creature race) - pos=minEx{pos1,pos2} - if pos ~=0 then - - if(pos-p>250) then - --this here does not work yet... - --[[pos =offsets.find(p,CALL) - print(string.format("Distance to call:%x",pos-p)) - print(string.format("Call: %x",signDword(engine.peekd(pos+1)+pos))) - pos=analyzeF(signDword(signDword(engine.peekd(pos+1)+pos))) - - print(string.format("Cmp @:%x",pos))]]-- - print(string.format("skipping %x... Cmp too far away (dist=%i)",p,pos-p)) - else - --print(string.format("Found at %x, simple compare",pos)) - --print(string.format("Distance =%x",pos-p)) - --patch compares - - pokeCall(pos) - end - else - break - end - myp=myp+pos+6 - if myp-p >250 then break end - until false - - end -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship/plugin.lua b/plugins/Dfusion/luafiles/friendship/plugin.lua deleted file mode 100644 index 695ddf05e..000000000 --- a/plugins/Dfusion/luafiles/friendship/plugin.lua +++ /dev/null @@ -1,18 +0,0 @@ -if not(FILE) then - --sanity test - --print("race num:"..engine.peekw(offsets.getEx("CurrentRace"))) - --print(string.format("%x vs %x",offsets.getEx("CurrentRace"),VersionInfo.getGroup("Creatures"):getAddress("current_race"))) - print("Race num:"..df.global.ui.race_id) - print("Your current race is:"..GetRaceToken(df.global.ui.race_id)) - print("If this is wrong please type 'q'") - if(getline()=='q') then - return - end - -end - -if not(FILE) then - names=ParseNames("dfusion/friendship/races.txt")--io.open("plugins/friendship/races.txt"):lines() - friendship_in.install(names) - friendship_in.patch() -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship/races.txt b/plugins/Dfusion/luafiles/friendship/races.txt deleted file mode 100644 index e4ed7c5a6..000000000 --- a/plugins/Dfusion/luafiles/friendship/races.txt +++ /dev/null @@ -1,8 +0,0 @@ -DWARF -GOBLIN -ELF -HUMAN -KOBOLD -GREMLIN -TIGERMAN -ANT_MAN \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship_civ/compile.bat b/plugins/Dfusion/luafiles/friendship_civ/compile.bat deleted file mode 100644 index 64d4d4440..000000000 --- a/plugins/Dfusion/luafiles/friendship_civ/compile.bat +++ /dev/null @@ -1 +0,0 @@ -as -anl --32 -o friendship_c.o friendship_c.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship_civ/friendship_c.asm b/plugins/Dfusion/luafiles/friendship_civ/friendship_c.asm deleted file mode 100644 index b2b49ee78..000000000 --- a/plugins/Dfusion/luafiles/friendship_civ/friendship_c.asm +++ /dev/null @@ -1,41 +0,0 @@ -.intel_syntax -eaxpart: -push eax -push ecx -jmp compare -ecxpart: -push eax -push ecx -mov eax,ecx - -compare: -push ebx -mov ebx,0xDEADBEEF #write a pointer to the list of allowed civs -mov ecx,2000 #write a number of allowed civs -loop1: -cmp [ebx+ecx*4],eax -jnz endok -dec ecx -cmp ecx ,-1 -jnz loop1 - -pop ebx - -jmp fail - -endok: -pop ebx - -cmp eax,eax -jmp endfinal -fail: - -xor ecx,ecx -xor eax,eax -inc eax -cmp eax,ebx -endfinal: - -pop ecx -pop eax -ret \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/friendship_civ/friendship_c.o b/plugins/Dfusion/luafiles/friendship_civ/friendship_c.o deleted file mode 100644 index d85134682ac1e464b944b3e3403babe96a43a8b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmeZaWM%+?JwVJ4X0bBrm84dbfY}hj07&&9@j*-l27?5>l*E!mG;wsU1B1Z1OTR|HW{tKy@S5H5q~QSv z%NvmqhxO7jb5fxO{rk_rkj|Kvm<QhwOUcg$@)Y4BX^ELRKn{Z%Tm+~lEi*4MM;9)F9Ficrm>EG~ ai3*TIkqIcp3{r>&kV6m@U)a=uTn_-GKtCM- diff --git a/plugins/Dfusion/luafiles/friendship_civ/init.lua b/plugins/Dfusion/luafiles/friendship_civ/init.lua deleted file mode 100644 index 5390829de..000000000 --- a/plugins/Dfusion/luafiles/friendship_civ/init.lua +++ /dev/null @@ -1,89 +0,0 @@ -friendship_civ={} -function friendship_civ.init() - friendship_civ.count=0x0f - friendship_civ.firsttime=true - local mypos=engine.getmod("Friendship_civ") - local modpos=0 - if mypos then - modpos=mypos - _,modsize=engine.loadobj("dfusion/friendship_civ/friendship_c.o") - _=nil - friendship_civ.firsttime=false - else - modpos,modsize=engine.loadmod("dfusion/friendship_civ/friendship_c.o","Friendship_civ",1024) - print(string.format("Loaded module @:%x",modpos)) - end - friendship_civ.modpos=modpos - friendship_civ.modsize=modsize -end -function friendship_civ.install(civs) - friendship_civ.init() - local count=0 - for _,v in pairs(civs) do - engine.poked(friendship_civ.modpos+friendship_civ.modsize+count*4,v) -- for some reason it compiled strangely - -- cmp word[ebx+ecx*2],ax -> cmp word[ebx+ecx*2+2],ax - count = count + 1 - end - engine.poked(friendship_civ.modpos+0x0a,friendship_civ.modpos+friendship_civ.modsize) -- set ptr to civ ids - engine.poked(friendship_civ.modpos+friendship_civ.count,count-1) -- set count of civs - SetExecute(friendship_civ.modpos) - - if(friendship_civ.firsttime) then - friendship_civ.patch() - end -end -function friendship_civ.getcivs() - if(friendship_civ.firsttime==nil)then - return nil - end - friendship_civ.init() - local count=engine.peekd(friendship_civ.modpos+friendship_civ.count)+1 - local ret={} - for i=0, count-1 do - table.insert(ret,engine.peekd(friendship_civ.modpos+friendship_civ.modsize+i*4)) - end - return ret -end -function friendship_civ.addciv(civ) --if called with nil add current civ :) - if civ==nil then - local cciv=engine.peekd(VersionInfo.getGroup("Creatures"):getAddress("current_civ")) - friendship_civ.install({cciv}) - return - end - local oldcivs=friendship_civ.getcivs() - oldcivs=oldcivs or {} - if type(civ)=="table" then - for k,v in ipairs(civ) do - table.insert(oldcivs,v) - end - else - table.insert(oldcivs,civ) - end - friendship_civ.install(oldcivs) -end -function friendship_civ.patch_call(off,iseax) - local calltrg=friendship_civ.modpos - if not iseax then - calltrg=calltrg+4 - end - engine.pokeb(off,0xe8) --this is a call - engine.poked(off+1,calltrg-off-5) --offset to call to (relative) - engine.pokeb(off+5,0x90) --nop -end -function friendship_civ.patch() - --UpdateRanges() - local civloc= VersionInfo.getGroup("Creatures"):getAddress("current_civ") - local pos1=offsets.findall(0,0x3B,0x05,DWORD_,civloc) --eax - for k,v in pairs(pos1) do print(string.format("%d %x",k,v)) end - local pos2=offsets.findall(0,0x3B,0x0D,DWORD_,civloc) --ecx - for k,v in pairs(pos2) do print(string.format("%d %x",k,v)) end - - for k,v in pairs(pos1) do - print(string.format("Patching eax compare %d: %x",k,v)) - friendship_civ.patch_call(v,true) - end - for k,v in pairs(pos2) do - print(string.format("Patching ecx compare %d: %x",k,v)) - friendship_civ.patch_call(v,false) - end -end diff --git a/plugins/Dfusion/luafiles/friendship_civ/plugin.lua b/plugins/Dfusion/luafiles/friendship_civ/plugin.lua deleted file mode 100644 index 12f334fcf..000000000 --- a/plugins/Dfusion/luafiles/friendship_civ/plugin.lua +++ /dev/null @@ -1,57 +0,0 @@ -fc_ui={} -fc_ui.menu=MakeMenu() -function fc_ui.get() - local mycivs=friendship_civ.getcivs() - if mycivs~= nil then - print(" Currently friendly civs:") - for k,v in pairs(mycivs) do - print(string.format("%d. %d",k,v)) - end - else - print(" Plugin no yet activated.") - end -end -function fc_ui.add() - print("Type in civ id to add (leave empty to add current, q cancels):") - local r - while r==nil and r~='q' do - r=io.stdin:read() - if r=="" then - r=nil - break - end - if r~='q' then r=tonumber(r) else - return - end - end - friendship_civ.addciv(r) -end -function fc_ui.remove() - local mycivs=friendship_civ.getcivs() - if mycivs~= nil then - print(" Currently friendly civs:") - for k,v in pairs(mycivs) do - print(string.format("%d. %d",k,v)) - end - else - print(" Plugin no yet activated, nothing to remove.") - return - end - print("Type in civ id to remove( q cancels):") - local r - while r==nil and r~='q' do - r=io.stdin:read() - if r~='q' then - r=tonumber(r) - if r>#mycivs then r=nil end - else - return - end - end - table.remove(mycivs,r) - friendship_civ.install(mycivs) -end -fc_ui.menu:add("Add civ",fc_ui.add) -fc_ui.menu:add("Get civs",fc_ui.get) -fc_ui.menu:add("Remove civ",fc_ui.remove) -fc_ui.menu:display() \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua deleted file mode 100644 index c8eebcd23..000000000 --- a/plugins/Dfusion/luafiles/init.lua +++ /dev/null @@ -1,92 +0,0 @@ -function dofile(filename) --safer dofile, with traceback (very usefull) - f,perr=loadfile(filename) - if f~=nil then - return safecall(f) - else - print(perr) - end -end -function dofile_silent(filename) --safer dofile, with traceback, no file not found error - f,perr=loadfile(filename) - if f~=nil then - return safecall(f) - else - if(string.sub(perr,1,11)~="cannot open") then --ugly hack - print(perr) - end - end -end -function loadall(t1) --loads all non interactive plugin parts, so that later they could be used - for k,v in pairs(t1) do - dofile_silent("dfusion/"..v[1].."/init.lua") - end -end -function mainmenu(t1) - while true do - print("No. Name Desc") - for k,v in pairs(t1) do - print(string.format("%3d %15s %s",k,v[1],v[2])) - end - local q=dfhack.lineedit("Select plugin to run (q to quit):") - if q=='q' then return end - q=tonumber(q) - if q~=nil then - if q>=1 and q<=#t1 then - if t1[q][3]==nil then - dofile("dfusion/"..t1[q][1].."/plugin.lua") - else - t1[q][3]() - end - end - end - end -end -function RunSaved() - print("Locating saves...") - local str=df.global.world.cur_savegame.save_dir - print("Current region:"..str) - str="data/save/"..str.."/dfusion/init.lua" - print("Trying to run:"..str) - dofile_silent(str) -end -dofile("dfusion/common.lua") -dofile("dfusion/utils.lua") -dofile("dfusion/offsets_misc.lua") -dofile("dfusion/editor.lua") - -unlockDF() -plugins={} -table.insert(plugins,{"simple_embark","A simple embark dwarf count editor"}) -table.insert(plugins,{"tools","some misc tools"}) -table.insert(plugins,{"embark","Multi race embark"}) -table.insert(plugins,{"friendship","Multi race fort enabler"}) ---[=[table.insert(plugins,{"items","A collection of item hacking tools"}) -table.insert(plugins,{"offsets","Find all offsets"}) - -table.insert(plugins,{"friendship_civ","Multi civ fort enabler"}) - - -table.insert(plugins,{"triggers","a function calling plug (discontinued...)"}) -table.insert(plugins,{"migrants","multi race imigrations"}) ---]=] ---table.insert(plugins,{"onfunction","run lua on some df function"}) ---table.insert(plugins,{"editor","edit internals of df",EditDF}) -table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved}) -table.insert(plugins,{"adv_tools","some tools for (mainly) adventurer hacking"}) -loadall(plugins) -dofile_silent("dfusion/initcustom.lua") - -local args={...} - - -local f,err=load(table.concat(args,' ')) -if f then - f() -else - dfhack.printerr(err) -end - -if not INIT then -mainmenu(plugins) -end - diff --git a/plugins/Dfusion/luafiles/migrants/compile.bat b/plugins/Dfusion/luafiles/migrants/compile.bat deleted file mode 100644 index 4d226851d..000000000 --- a/plugins/Dfusion/luafiles/migrants/compile.bat +++ /dev/null @@ -1 +0,0 @@ -as -anl --32 -o migrants.o migrants.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/migrants/init.lua b/plugins/Dfusion/luafiles/migrants/init.lua deleted file mode 100644 index 6fb315923..000000000 --- a/plugins/Dfusion/luafiles/migrants/init.lua +++ /dev/null @@ -1,62 +0,0 @@ ---install part -function migrants(names) -RaceTable=RaceTable or BuildNameTable() -mypos=engine.getmod("Migrants") -if mypos then - print("Migrant mod is running already @:"..mypos) - modpos=mypos - _,modsize=engine.loadobj("dfusion/migrants/migrants.o") - count=0 - for _,v in pairs(names) do - if RaceTable[v] == nil then - print("Failure, "..v.." not found!") - break --maybe some indication of failure? and cleanup? - end - engine.pokew(modpos+modsize+count*2+4,RaceTable[v]) - count = count + 1 - end - seedpos=modpos+modsize - engine.poked(seedpos,math.random(1234567)) -- seed the generator :) - engine.poked(modpos+0x1c,count) --max size for div - -else - modpos,modsize=engine.loadmod("dfusion/migrants/migrants.o","Migrants",400) - print(string.format("Loaded module @:%x",modpos)) - count=0 - for _,v in pairs(names) do - if RaceTable[v] == nil then - print("Failure, "..v.." not found!") - break --maybe some indication of failure? and cleanup? - end - engine.pokew(modpos+modsize+count*2+4,RaceTable[v]) - - count = count + 1 - end - - seedpos=modpos+modsize - engine.poked(modpos+0x04,seedpos) - engine.poked(modpos+0x15,seedpos) - - engine.poked(seedpos,math.random(1234567)) -- seed the generator :) - engine.poked(modpos+0x1c,count) --max size for div - - engine.poked(modpos+0x26,seedpos+4) --start of array - - --patch part - --pos=62873C+DF - -- pattern: A1,DWORD_,"CURRENTRACE",56,89,ANYBYTE,ANYBYTE,34,e8 - _,raceoff=df.sizeof(df.global.ui:_field('race_id')) - pos=offsets.find(offsets.base(),0xa1,DWORD_,raceoff,0x56,0x89,ANYBYTE,ANYBYTE,0x34,0xe8) - function pokeCall(off) - engine.pokeb(off,0xe8) - engine.poked(off+1,modpos-off-5) - end - if pos~=0 then - print(string.format("Found @:%x",pos)) - pokeCall(pos) - else - print("Not found patch location!!!") - end - end - -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/migrants/migrants.asm b/plugins/Dfusion/luafiles/migrants/migrants.asm deleted file mode 100644 index 646497665..000000000 --- a/plugins/Dfusion/luafiles/migrants/migrants.asm +++ /dev/null @@ -1,20 +0,0 @@ -.intel_syntax -pushfd -push ebx -push edx -mov eax,[0xdeadbeef] # get old seed -mov ebx,1103515245 -#mul 1103515245 -mul ebx -add eax,12345 -mov [0xdeadbeef],eax #put seed back...thus generation rnd is complete - -xor edx,edx -mov ebx,2000 #put size of array here -div ebx #why oh why there is no div const? compiler prob makes some xor/add magic -movzx eax,word ptr[0xdeadbeef+edx*2] -pop edx -pop ebx - -popfd -ret diff --git a/plugins/Dfusion/luafiles/migrants/migrants.o b/plugins/Dfusion/luafiles/migrants/migrants.o deleted file mode 100644 index 30b5b14f03cd8e110b91123e0631f8e0b3378ed0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmeZaWM%+?JwVI>X0bBrm84dbfY}hj07&&9@j*-l27?5>l*E!mG;wsU1B1Zh*T58Y}8WPdP$)nF)LK=uPD#F#;bp#c^k F9{~H9FI)fs diff --git a/plugins/Dfusion/luafiles/migrants/plugin.lua b/plugins/Dfusion/luafiles/migrants/plugin.lua deleted file mode 100644 index 651e4900c..000000000 --- a/plugins/Dfusion/luafiles/migrants/plugin.lua +++ /dev/null @@ -1,5 +0,0 @@ - -if not(FILE) then - names=ParseNames("dfusion/migrants/races.txt")--io.open("plugins/migrants/races.txt"):lines() - migrants(names) -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/migrants/races.txt b/plugins/Dfusion/luafiles/migrants/races.txt deleted file mode 100644 index 9a3fa6cf6..000000000 --- a/plugins/Dfusion/luafiles/migrants/races.txt +++ /dev/null @@ -1,29 +0,0 @@ -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -ELF -HUMAN -DWARF -GREMLIN -KOBOLD -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -DWARF -ELF -HUMAN -DWARF -GREMLIN -KOBOLD -DEMON_13 \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/offsets/plugin.lua b/plugins/Dfusion/luafiles/offsets/plugin.lua deleted file mode 100644 index b9e2fc441..000000000 --- a/plugins/Dfusion/luafiles/offsets/plugin.lua +++ /dev/null @@ -1,2 +0,0 @@ -offsets.searchoffsets() -offsets.save() \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/offsets_misc.lua b/plugins/Dfusion/luafiles/offsets_misc.lua deleted file mode 100644 index 8bca50000..000000000 --- a/plugins/Dfusion/luafiles/offsets_misc.lua +++ /dev/null @@ -1,48 +0,0 @@ -offsets=offsets or {} -function offsets.find(startoffset,...) - -- [=[ - if startoffset== 0 then - local text=GetTextRegion() - --print("searching in:"..text.name) - startoffset=text.start - endadr=text["end"] - else - local reg=GetRegionIn(startoffset) - --print("searching in:"..reg.name) - if reg==nil then - print(string.format("Warning: memory range for search @:%x not found!",startoffset)) - return 0 - end - endadr=reg["end"] - end - --]=] - --print(string.format("Searching (%x->%x)",startoffset,endadr)) - local h=hexsearch(startoffset,endadr,...) - local pos=h:find() - h=nil - return pos -end -function offsets.findall(startoffset,...) - local endadr; - if startoffset== 0 then - local text=GetTextRegion() - --print("searching in:"..text.name) - startoffset=text.start - endadr=text["end"] - else - local reg=GetRegionIn(startoffset) - --print("searching in:"..reg.name) - endadr=reg["end"] - end - local h=hexsearch(startoffset,endadr,...) - local pos=h:findall() - h=nil - return pos -end -function offsets.base() - return Process.getBase() -end -function offsets.getvectors() - return findVectors() -end -ADDRESS=ANYDWORD \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/simple_embark/plugin.lua b/plugins/Dfusion/luafiles/simple_embark/plugin.lua deleted file mode 100644 index abc3530c2..000000000 --- a/plugins/Dfusion/luafiles/simple_embark/plugin.lua +++ /dev/null @@ -1,23 +0,0 @@ -function simple_embark(num) - local stoff=dfhack.internal.getAddress('start_dwarf_count') - print("Starting dwarves found:"..df.reinterpret_cast('int32_t', stoff).value) - local tmp_val=df.new('int32_t') - local size,pos=tmp_val:sizeof() - tmp_val.value=num - local ret=dfhack.internal.patchMemory(stoff,tmp_val,size) - if ret then - print("Success!") - else - qerror("Failed to patch in number") - end -end -if not(FILE) then -print("Type in new amount (more than 6, less than 15000):") - repeat - ans=tonumber(io.read()) - if ans==nil or not(ans<=15000 and ans>0) then - print("incorrect choice") - end - until ans~=nil and (ans<=15000 and ans>0) - simple_embark(ans) -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua deleted file mode 100644 index a8c787ffb..000000000 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ /dev/null @@ -1,511 +0,0 @@ -local ms=require "memscan" -tools={} -tools.menu=MakeMenu() -function tools.setrace(name) - RaceTable=BuildNameTable() - print("Your current race is:"..GetRaceToken(df.global.ui.race_id)) - local id - if name == nil then - print("Type new race's token name in full caps (q to quit):") - repeat - entry=getline() - if entry=="q" then - return - end - id=RaceTable[entry] - until id~=nil - else - id=RaceTable[name] - if id==nil then - error("Name not found!") - end - end - df.global.ui.race_id=id -end -tools.menu:add("Set current race",tools.setrace) -function tools.GiveSentience(names) - RaceTable=RaceTable or BuildNameTable() --slow.If loaded don't load again - if names ==nil then - ids={} - print("Type race's token name in full caps to give sentience to:") - repeat - entry=getline() - id=RaceTable[entry] - until id~=nil - table.insert(ids,id) - else - ids={} - for _,name in pairs(names) do - id=RaceTable[name] - table.insert(ids,id) - end - end - for _,id in pairs(ids) do - local races=df.global.world.raws.creatures.all - - local castes=races[id].caste - print(string.format("Caste count:%i",castes.size)) - for i =0,#castes-1 do - - print("Caste name:"..castes[i].caste_id.."...") - - local flags=castes[i].flags - --print(string.format("%x",flagoffset)) - if flags.CAN_SPEAK then - print("\tis sentient.") - else - print("\tnon sentient. Allocating IQ...") - flags.CAN_SPEAK=true - end - end - end -end -tools.menu:add("Give Sentience",tools.GiveSentience) -function tools.embark() --windows only? - local seg=ms.get_code_segment() - local idx,off - - idx,off=seg.uint8_t:find_one{0x66, 0x83, 0x7F ,0x1A ,0xFF,0x74,0x04} - if idx then - local tmp_val=df.new('uint8_t',2) - tmp_val[0]=0x90 - tmp_val[1]=0x90 - local size,pos=tmp_val:sizeof() - local ret=dfhack.internal.patchMemory(off+5,pos,size*2) - if ret then - print("Found and patched:",off+5) - else - print("Patching failed at:",off+5) - end - tmp_val:delete() - else - qerror("Offset for embark patch not found!") - end -end -if WINDOWS then -tools.menu:add("Embark anywhere",tools.embark) -end -function tools.getCreatureId(vector) --redo it to getcreature by name/id or something - tnames={} - rnames={} - --[[print("vector1 size:"..vector:size()) - print("vector2 size:"..vector2:size())]]-- - for i=0,vector:size()-1 do - --print(string.format("%x",vector:getval(i))) - - local name=engine.peek(vector:getval(i),ptt_dfstring):getval() - local lid= tools.getlegendsid(vector:getval(i)) - if lid ~=0 then - print(i..")*Creature Name:"..name.." race="..engine.peekw(vector:getval(i)+ptr_Creature.race.off).." legendid="..lid) - else - print(i..") Creature Name:"..name.." race="..engine.peekw(vector:getval(i)+ptr_Creature.race.off)) - end - if name ~="" and name~=nil then - tnames[i]=name - rnames[name]=i - end - end - print("=====================================") - print("type in name or number:") - r=getline() - if tonumber(r) ==nil then - indx=rnames[r] - if indx==nil then return end - else - r=tonumber(r) - if rLOVE), q to quit:") - names={} - repeat - w=getline(); - - if rwords[w]~=nil then - table.insert(names,w) - print("--added--") - end - - until w=='q' - end - - tnames={} - for _,v in pairs(names) do - if rwords[v] ~=nil then - table.insert(tnames,rwords[v]) --get word numbers - end - end - - local offsites=engine.peekd(offsets.getEx("SiteData"))+0x120 - snames={" pfort"," dfort"," cave","mohall","forest","hamlet","imploc"," lair"," fort"," camp"} - vector=engine.peek(offsites,ptr_vector) - print("Number of sites:"..vector:size()) - print("List of hits:") - for i =0,vector:size()-1 do - off=vector:getval(i) - - good=true - r="" - hits=0 - sname=engine.peek(off,ptr_site.name) - for k=0,6 do - vnum=sname[k]--engine.peekd(off+0x38+k*4) - tgood=false - - if vnum~=0xFFFFFFFF then - --print(string.format("%x",vnum)) - if names[vnum]~=nil then - r=r..names[vnum].." " - end - for _,v in pairs(tnames) do - if vnum==v then - tgood=true - --print("Match") - hits=hits+1 - break - end - end - if not tgood then - good=false - end - end - end - - if(good) and (hits>0)then - --if true then - --print("=====================") - typ=engine.peek(off,ptr_site.type)--engine.peekw(off+0x78) - flg=engine.peekd(engine.peek(off,ptr_site.flagptr)) - --flg=engine.peekd(off+224) - --flg2=engine.peekw(off) - --tv=engine.peek(off+0x84,ptr_vector) - --tv2=engine.peek(off+0xA4,ptr_vector) - - print(string.format("%d)%s off=%x type=%s\t flags=%x",i,r,off,snames[typ+1],flg)) - - if i%100==99 then - r=getline() - end - - end - end - print("Type which to change (q cancels):") - repeat - r=getline() - n=tonumber(r) - if(r=='q') then return end - until n~=nil - return vector:getval(n) -end -function tools.changesite(names) - off=tools.getsite(names) - snames={"Mountain halls (yours)","Dark fort","Cave","Mountain hall (NPC)","Forest retreat","Hamlet","Important location","Lair","Fort","Camp"} - - print("Type in the site type (q cancels):") - for k,v in pairs(snames) do - print((k-1).."->"..v) - end - repeat - r=getline() - n2=tonumber(r) - if(r=='q') then return end - until n2~=nil - --off=vector:getval(n) - print(string.format("%x->%d",off,n2)) - engine.poke(off,ptr_site.type,n2) -end -function tools.project(unit,trg) - if unit==nil then - unit=getCreatureAtPointer() - end - - if unit==nil then - error("Failed to project unit. Unit not selected/valid") - end - -- todo: add projectile to world, point to unit, add flag to unit, add gen-ref to projectile. - local p=df.proj_unitst:new() - local startpos={x=unit.pos.x,y=unit.pos.y,z=unit.pos.z} - p.origin_pos=startpos - p.target_pos=trg - p.cur_pos=startpos - p.prev_pos=startpos - p.unit=unit - --- wtf stuff - p.unk14=100 - p.unk16=-1 - p.unk23=-1 - p.fall_delay=5 - p.fall_counter=5 - p.collided=true - -- end wtf - local citem=df.global.world.proj_list - local maxid=1 - local newlink=df.proj_list_link:new() - newlink.item=p - while citem.item~= nil do - if citem.item.id>maxid then maxid=citem.item.id end - if citem.next ~= nil then - citem=citem.next - else - break - end - end - p.id=maxid+1 - newlink.prev=citem - citem.next=newlink - - local proj_ref=df.general_ref_projectile:new() - proj_ref.projectile_id=p.id - unit.refs:insert(#unit.refs,proj_ref) - unit.flags1.projectile=true -end -function tools.empregnate(unit) - if unit==nil then - unit=getSelectedUnit() - end - - if unit==nil then - unit=getCreatureAtPos(getxyz()) - end - - if unit==nil then - error("Failed to empregnate. Unit not selected/valid") - end - if unit.curse then - unit.curse.add_tags2.STERILE=false - end - local genes = unit.appearance.genes - if unit.relations.pregnancy_ptr == nil then - print("creating preg ptr.") - if false then - print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_ptr")))) - return - end - unit.relations.pregnancy_ptr = { new = true, assign = genes } - end - local ngenes = unit.relations.pregnancy_ptr - if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then - print("Array sizes incorrect, fixing.") - ngenes:assign(genes); - end - print("Setting preg timer.") - unit.relations.pregnancy_timer=10 - unit.relations.pregnancy_mystery=1 -end -tools.menu:add("Empregnate",tools.empregnate) -function tools.changeflags(names) - myflag_pattern=ptt_dfflag.new(3*8) - off=tools.getsite(names) - offflgs=engine.peek(off,ptr_site.flagptr) - q='' - print(string.format("Site offset %x flags offset %x",off,offflgs)) - repeat - print("flags:") - - --off=vector:getval(n) - flg=engine.peek(offflgs,myflag_pattern) - r="" - for i=0,3*8-1 do - if flg:get(i)==1 then - r=r.."x" - else - r=r.."o" - end - if i%8==7 then - print(i-7 .."->"..r) - r="" - end - end - print("Type number to flip, or 'q' to quit.") - q=getline() - n2=tonumber(q) - if n2~=nil then - - flg:flip(n2) - engine.poke(offflgs,myflag_pattern,flg) - end - until q=='q' -end -function tools.hostilate() - vector=engine.peek(offsets.getEx("CreatureVec"),ptr_vector) - id=engine.peekd(offsets.getEx("CreaturePtr")) - print(string.format("Vec:%d cr:%d",vector:size(),id)) - off=vector:getval(id) - crciv=engine.peek(off,ptr_Creature.civ) - print("Creatures civ:"..crciv) - curciv=engine.peekd(offsets.getEx("CurrentRace")-12) - print("My civ:"..curciv) - if curciv==crciv then - print("Friendly-making enemy") - engine.poke(off,ptr_Creature.civ,-1) - flg=engine.peek(off,ptr_Creature.flags) - flg:set(17,0) - print("flag 51:"..tostring(flg:get(51))) - engine.poke(off,ptr_Creature.flags,flg) - else - print("Enemy- making friendly") - engine.poke(off,ptr_Creature.civ,curciv) - flg=engine.peek(off,ptr_Creature.flags) - flg:set(17,1) - flg:set(19,0) - engine.poke(off,ptr_Creature.flags,flg) - end -end -function tools.mouseBlock() - local xs,ys,zs - xs,ys,zs=getxyz() - xs=math.floor(xs/16) - ys=math.floor(ys/16) - print("Mouse block is:"..xs.." "..ys.." "..zs) -end -function tools.fixwarp() - local mapoffset=offsets.getEx("WorldData")--0x131C128+offsets.base() - local x=engine.peek(mapoffset+24,DWORD) - local y=engine.peek(mapoffset+28,DWORD) - local z=engine.peek(mapoffset+32,DWORD) - --vec=engine.peek(mapoffset,ptr_vector) - - print("Blocks loaded:"..x.." "..y.." "..z) - print("Select type:") - print("1. All (SLOW)") - print("2. range (x0 x1 y0 y1 z0 z1)") - print("3. One block around pointer") - print("anything else- quit") - q=getline() - n2=tonumber(q) - if n2==nil then return end - if n2>3 or n2<1 then return end - local xs,xe,ys,ye,zs,ze - if n2==1 then - xs=0 - xe=x-1 - ys=0 - ye=y-1 - zs=0 - ze=z-1 - elseif n2==2 then - print("enter x0:") - xs=tonumber(getline()) - print("enter x1:") - xe=tonumber(getline()) - print("enter y0:") - ys=tonumber(getline()) - print("enter y1:") - ye=tonumber(getline()) - print("enter z0:") - zs=tonumber(getline()) - print("enter z1:") - ze=tonumber(getline()) - function clamp(t,vmin,vmax) - if t> vmax then return vmax end - if t< vmin then return vmin end - return t - end - xs=clamp(xs,0,x-1) - ys=clamp(ys,0,y-1) - zs=clamp(zs,0,z-1) - xe=clamp(xe,xs,x-1) - ye=clamp(ye,ys,y-1) - ze=clamp(ze,zs,z-1) - else - xs,ys,zs=getxyz() - xs=math.floor(xs/16) - ys=math.floor(ys/16) - xe=xs - ye=ys - ze=zs - end - local xblocks=engine.peek(mapoffset,DWORD) - local flg=bit.bnot(bit.lshift(1,3)) - for xx=xs,xe do - local yblocks=engine.peek(xblocks+xx*4,DWORD) - for yy=ys,ye do - local zblocks=engine.peek(yblocks+yy*4,DWORD) - for zz=zs,ze do - local myblock=engine.peek(zblocks+zz*4,DWORD) - if myblock~=0 then - for i=0,255 do - local ff=engine.peek(myblock+0x67c+i*4,DWORD) - ff=bit.band(ff,flg) --set 14 flag to 1 - engine.poke(myblock+0x67c+i*4,DWORD,ff) - end - end - end - print("Blocks done:"..xx.." "..yy) - end - end -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/tools/plugin.lua b/plugins/Dfusion/luafiles/tools/plugin.lua deleted file mode 100644 index 8eaefd44d..000000000 --- a/plugins/Dfusion/luafiles/tools/plugin.lua +++ /dev/null @@ -1,8 +0,0 @@ -if not(FILE) then - --tools.menu:add("Change site type",tools.changesite) - --tools.menu:add("Change site flags",tools.changeflags) - --tools.menu:add("Hostilate creature",tools.hostilate) - --tools.menu:add("Print current mouse block",tools.mouseBlock) - - tools.menu:display() -end \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/utils.lua b/plugins/Dfusion/luafiles/utils.lua deleted file mode 100644 index d481f5cfc..000000000 --- a/plugins/Dfusion/luafiles/utils.lua +++ /dev/null @@ -1 +0,0 @@ -function findVectorsSized(size) local ret={} local text=GetTextRegion() for k,v in pairs(offsets.getvectors()) do if GetRegionIn2(k)~=nil then --if v>4 then local tv=engine.peek(k,ptr_vector) if tv:size() == size then print(string.format("%x is size %d",k,size)) table.insert(ret,k) end end end return ret end function findMaterial(mattype,matname) --currently only stones local tbl=BuildMaterialTable() return tbl[matname] end function iter(tbl) if getmetatable(tbl) ~=nil then if getmetatable(tbl).__next~= nil then return getmetatable(tbl).__next,tbl else return getmetatable(tbl).__pairs(tbl) or pairs(tbl) end else return pairs(tbl) end end \ No newline at end of file diff --git a/plugins/lua/dfusion/embark.lua b/plugins/lua/dfusion/embark.lua index 578c0b903..6e5df4eef 100644 --- a/plugins/lua/dfusion/embark.lua +++ b/plugins/lua/dfusion/embark.lua @@ -3,22 +3,34 @@ local dfu=require("plugins.dfusion") local ms=require("memscan") local MAX_RACES=100 CustomEmbark=defclass(CustomEmbark,dfu.BinaryPlugin) -CustomEmbark.name="CustomEmbark" - -print(t) local myos=dfhack.getOSType() if myos=="windows" then CustomEmbark.ATTRS{filename="hack/lua/plugins/dfusion/embark.o",name="CustomEmbark",race_caste_data=DEFAULT_NIL} - CustomEmbark.class_status="valid, not installed" - function CustomEmbark:install() - local stoff=dfhack.internal.getAddress('start_dwarf_count') - - if #self.race_caste_data<7 then + function CustomEmbark:parseRaces(races) + if #races<7 then error("caste and race count must be bigger than 6") end - if #self.race_caste_data>MAX_RACES then + if #races>MAX_RACES then error("caste and race count must be less then "..MAX_RACES) end + local n_to_id=require("plugins.dfusion.tools").build_race_names() + + local ids={} + for k,v in pairs(races) do + local race=v[1] or v + ids[k]={} + ids[k][1]=n_to_id[race] + if ids[k][1]==nil then qerror(race.." not found!") end + ids[k][2]=v[2] or -1 + end + self.race_caste_data=ids + end + function CustomEmbark:install(race_caste_data) + local stoff=dfhack.internal.getAddress('start_dwarf_count') + if race_caste_data~=nil then + self:parseRaces(race_caste_data) + end + if stoff==nil then error("address for start_dwarf_count not found!") end @@ -40,14 +52,14 @@ if myos=="windows" then local call_data={0x90,0x90} local _,data_offset=df.sizeof(self.data) dfu.concatTables(call_data,dfu.makeCall(trg_offset+2,data_offset)) - self.call_patch=dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} + self.call_patch=self.call_patch or dfu.BinaryPatch{pre_data=needle,data=call_data,address=trg_offset,name="custom_embark_call_patch"} needle={0x83,0xc8,0xff} -- or eax, 0xFF local _,caste_offset=mem.uint8_t:find(needle,mem.uint8_t:addr2idx(trg_offset),nil) if caste_offset==nil or caste_offset-stoff>1000 then error("Caste change code not found or found too far!") end - self.disable_castes=dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} + self.disable_castes=self.disable_castes or dfu.BinaryPatch{pre_data={0x83,0xc8,0xff},data={0x90,0x90,0x90},address=caste_offset,name="custom_embark_caste_disable"} self.disable_castes:apply() @@ -66,16 +78,11 @@ if myos=="windows" then self.call_patch:apply() self.installed=true end + function CustomEmbark:setEmbarkParty(racesAndCastes) local stoff=dfhack.internal.getAddress('start_dwarf_count') - if #racesAndCastes<7 then - error("caste and race count must be bigger than 6") - end - if #racesAndCastes>MAX_RACES then - error("caste and race count must be less then "..MAX_RACES) - end - self.race_caste_data=racesAndCastes + if self.dwarfcount== nil then self.dwarfcount=dfu.BinaryPatch{pre_data=dfu.dwordToTable(7),data=dfu.dwordToTable(#self.race_caste_data),address=stoff,name="custom_embark_embarkcount"} self.dwarfcount:apply() @@ -84,8 +91,9 @@ if myos=="windows" then end local caste_array=self:get_or_alloc("caste_array","uint16_t",MAX_RACES) local race_array=self:get_or_alloc("race_array","uint16_t",MAX_RACES) + for k,v in ipairs(self.race_caste_data) do - caste_array[k-1]=v[2] + caste_array[k-1]=v[2] or -1 race_array[k-1]=v[1] end end @@ -103,48 +111,14 @@ if myos=="windows" then self.dwarfcount:remove() end end - function CustomEmbark:edit() - local data=self.race_caste_data or {} - print(string.format("Old race count:%d",#data)) - local endthis=false - print("current:") - while(not endthis) do - print(" # RaceId Race name Caste num") - for k,v in pairs(data) do - local name=df.creature_raw.find(v[1]).creature_id or "" - print(string.format("%3d. %6d %20s %d",k,v[1],name,v[2])) - end - print("a- add, r-remove, c-cancel, s-save and update") - local choice=io.stdin:read() - if choice=='a' then - print("Enter new race then caste ids:") - local race=tonumber(io.stdin:read()) - local caste=tonumber(io.stdin:read()) - if race and caste then - table.insert(data,{race,caste}) - else - print("input parse error") - end - elseif choice=='r' then - print("enter number to remove:") - local num_rem=tonumber(io.stdin:read()) - if num_rem~=nil then - table.remove(data,num_rem) - end - elseif choice=='c' then - endthis=true - elseif choice=='s' then - endthis=true - if self.installed then - self:setEmbarkParty(data) - else - self.race_caste_data=data - self:install() - end - end + function CustomEmbark:unload() + self:uninstall() + if Embark~=nil then + Embark=nil end end + Embark=Embark or CustomEmbark() else - CustomEmbark.class_status="invalid, os not supported" + CustomEmbark.status=function() return"invalid, os not supported" end end return _ENV \ No newline at end of file diff --git a/plugins/lua/dfusion/friendship.lua b/plugins/lua/dfusion/friendship.lua index 70a1ceea4..93a370c86 100644 --- a/plugins/lua/dfusion/friendship.lua +++ b/plugins/lua/dfusion/friendship.lua @@ -35,7 +35,7 @@ function FriendshipRainbow:find_all() dfu.concatTables(locations,self:find_one(code,{0x0f,0xbf,reg},crace)) --movsx reg,[ptr] dfu.concatTables(locations,self:find_one(code,{0x66,0x8b,reg},crace)) --mov reg,[ptr] end - printall(locations) + return self:filter_locations(code,locations) end function FriendshipRainbow:filter_locations(codesg,locations) @@ -107,5 +107,6 @@ function FriendshipRainbow:install(races) local addr=self:move_to_df() self:patchCalls(addr) self.installed=true - end +end +Friendship=Friendship or FriendshipRainbow() return _ENV \ No newline at end of file From 856c9ebd4b7bfb09a57576cb73763c1ead00baa1 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 Nov 2012 12:39:49 +0200 Subject: [PATCH 60/79] Added save specific scripts to lua interpreter script, also better error reporting. --- scripts/lua.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/scripts/lua.lua b/scripts/lua.lua index 497498e86..556962347 100644 --- a/scripts/lua.lua +++ b/scripts/lua.lua @@ -1,9 +1,26 @@ local args={...} if args[1]=="--file" or args[1]=="-f" then - local f=loadfile (args[2]) + local f,err=loadfile (args[2]) + if f==nil then + qerror(err) + end + dfhack.pcall(f,table.unpack(args,3)) +elseif args[1]=="--save" or args[1]=="-s" then + if df.global.world.cur_savegame.save_dir=="" then + qerror("Savefile not loaded") + end + local fname=args[2] or "dfhack.lua" + fname=string.format("data/save/%s/%s",df.global.world.cur_savegame.save_dir,fname) + local f,err=loadfile (fname) + if f==nil then + qerror(err) + end dfhack.pcall(f,table.unpack(args,3)) elseif args[1]~=nil then - local f=load(args[1],'=(lua command)', 't') + local f,err=load(args[1],'=(lua command)', 't') + if f==nil then + qerror(err) + end dfhack.pcall(f,table.unpack(args,2)) else dfhack.interpreter("lua","lua.history") From d5c31942b50accc840026182100c48928d76641f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 Nov 2012 10:58:05 +0400 Subject: [PATCH 61/79] Add a way to only count locally-made items in workflow. --- Readme.html | 661 ++++++++++++++++++++++----------------- Readme.rst | 82 ++++- dfhack.init-example | 23 +- plugins/lua/workflow.lua | 11 +- plugins/workflow.cpp | 95 ++++-- scripts/gui/workflow.lua | 11 +- 6 files changed, 550 insertions(+), 333 deletions(-) diff --git a/Readme.html b/Readme.html index cd073459c..16f3aed5a 100644 --- a/Readme.html +++ b/Readme.html @@ -337,197 +337,201 @@ access DF memory and allow for easier development of new tools.

  • Introduction
  • Getting DFHack
  • Compatibility
  • -
  • Installation/Removal
  • -
  • Using DFHack
      -
    • Patched binaries
    • +
    • Installation/Removal
    • -
    • Something doesn't work, help!
    • -
    • The init file
        -
      • Setting keybindings
      • +
      • Using DFHack
      • -
      • Commands
          -
        • Game progress
            -
          • die
          • -
          • forcepause
          • -
          • nopause
          • -
          • fastdwarf
          • +
          • Something doesn't work, help!
          • +
          • The init file
          • -
          • Game interface
              -
            • follow
            • -
            • tidlers
            • -
            • twaterlvl
            • -
            • copystock
            • -
            • rename
            • +
            • Commands
                +
              • Game progress
              • -
              • Adventure mode
                  -
                • adv-bodyswap
                • -
                • advtools
                • +
                • Game interface
                • -
                • Map modification
                    -
                  • changelayer
                  • -
                  • changevein
                  • -
                  • changeitem
                  • -
                  • colonies
                  • -
                  • deramp (by zilpin)
                  • -
                  • feature
                  • -
                  • liquids
                  • -
                  • liquids-here
                  • -
                  • tiletypes
                  • -
                  • tiletypes-commands
                  • -
                  • tiletypes-here
                  • -
                  • tiletypes-here-point
                  • -
                  • tubefill
                  • -
                  • extirpate
                  • -
                  • grow
                  • -
                  • immolate
                  • -
                  • regrass
                  • -
                  • weather
                  • +
                  • Adventure mode
                  • -
                  • Map inspection
                      -
                    • cursecheck
                    • -
                    • flows
                    • -
                    • probe
                    • -
                    • prospect
                        -
                      • Pre-embark estimate
                      • +
                      • Map modification
                      • -
                      • reveal
                      • -
                      • unreveal
                      • -
                      • revtoggle
                      • -
                      • revflood
                      • -
                      • revforget
                      • -
                      • showmood
                      • +
                      • Map inspection
                          +
                        • cursecheck
                        • +
                        • flows
                        • +
                        • probe
                        • +
                        • prospect
                        • -
                        • Designations
                        • -
                        • Cleanup and garbage disposal
                            -
                          • clean
                          • -
                          • spotclean
                          • -
                          • autodump
                          • -
                          • autodump-destroy-here
                          • -
                          • autodump-destroy-item
                          • -
                          • cleanowned
                          • +
                          • Designations
                          • -
                          • Bugfixes
                              -
                            • drybuckets
                            • -
                            • fixdiplomats
                            • -
                            • fixmerchants
                            • -
                            • fixveins
                            • -
                            • tweak
                            • -
                            • fix-armory
                            • +
                            • Cleanup and garbage disposal
                            • -
                            • Mode switch and reclaim
                                -
                              • lair
                              • -
                              • mode
                              • +
                              • Bugfixes
                              • -
                              • Visualizer and data export
                                  -
                                • ssense / stonesense
                                • -
                                • mapexport
                                • -
                                • dwarfexport
                                • +
                                • Mode switch and reclaim
                                • -
                                • Job management
                                    -
                                  • job
                                  • -
                                  • job-material
                                  • -
                                  • job-duplicate
                                  • -
                                  • workflow
                                      -
                                    • Function
                                    • -
                                    • Constraint examples
                                    • +
                                    • Visualizer and data export
                                    • +
                                    • Job management
                                        +
                                      • job
                                      • +
                                      • job-material
                                      • +
                                      • job-duplicate
                                      • +
                                      • workflow
                                      • -
                                      • Fortress activity management
                                          -
                                        • seedwatch
                                        • -
                                        • zone
                                        • -
                                        • autonestbox
                                        • -
                                        • autobutcher
                                        • -
                                        • autolabor
                                        • +
                                        • Fortress activity management
                                            +
                                          • seedwatch
                                          • +
                                          • zone
                                          • -
                                          • Other
                                          • +
                                          • Other
                                          • -
                                          • Scripts
                                          • -
                                          • In-game interface tools
                                              -
                                            • Dwarf Manipulator
                                            • -
                                            • Search
                                            • -
                                            • gui/liquids
                                            • -
                                            • gui/mechanisms
                                            • -
                                            • gui/rename
                                            • -
                                            • gui/room-list
                                            • -
                                            • gui/choose-weapons
                                            • -
                                            • gui/guide-path
                                            • -
                                            • gui/workshop-job
                                            • -
                                            • gui/workflow
                                            • -
                                            • gui/assign-rack
                                            • +
                                            • Scripts
                                            • -
                                            • Behavior Mods
                                                -
                                              • Siege Engine
                                                  -
                                                • Rationale
                                                • -
                                                • Configuration UI
                                                • +
                                                • In-game interface tools
                                                • -
                                                • Power Meter
                                                • -
                                                • Steam Engine @@ -570,9 +574,29 @@ remove the other DFHack files
                                                • The stonesense plugin might require some additional libraries on Linux.

                                                  If any of the plugins or dfhack itself refuses to load, check the stderr.log file created in your DF folder.

                                                  +
                                                  +

                                                  Getting started

                                                  +

                                                  If DFHack is installed correctly, it will automatically pop up a console +window once DF is started as usual on windows. Linux and Mac OS X require +running the dfhack script from the terminal, and will use that terminal for +the console.

                                                  +

                                                  NOTE: The dfhack-run executable is there for calling DFHack commands in +an already running DF+DFHack instance from external OS scripts and programs, +and is not the way how you use DFHack normally.

                                                  +

                                                  DFHack has a lot of features, which can be accessed by typing commands in the +console, or by mapping them to keyboard shortcuts. Most of the newer and more +user-friendly tools are designed to be at least partially used via the latter +way.

                                                  +

                                                  In order to set keybindings, you have to create a text configuration file +called dfhack.init; the installation comes with an example version called +dfhack.init-example, which is fully functional, covers all of the recent +features and can be simply renamed to dfhack.init. You are encouraged to look +through it to learn which features it makes available under which key combinations.

                                                  +

                                                  For more information, refer to the rest of this document.

                                                  +
                                                  -

                                                  Using DFHack

                                                  +

                                                  Using DFHack

                                                  DFHack basically extends what DF can do with something similar to the drop-down console found in Quake engine games. On Windows, this is a separate command line window. On linux, the terminal used to launch the dfhack script is taken over @@ -595,7 +619,7 @@ they are re-created every time it is loaded.

                                                  Interactive commands like 'liquids' cannot be used as hotkeys.

                                                  Most of the commands come from plugins. Those reside in 'hack/plugins/'.

                                                  -

                                                  Patched binaries

                                                  +

                                                  Patched binaries

                                                  On linux and OSX, users of patched binaries may have to find the relevant section in symbols.xml, and add a new line with the checksum of their executable:

                                                  @@ -624,7 +648,7 @@ system console:

                                                  -

                                                  Something doesn't work, help!

                                                  +

                                                  Something doesn't work, help!

                                                  First, don't panic :) Second, dfhack keeps a few log files in DF's folder - stderr.log and stdout.log. You can look at those and possibly find out what's happening. @@ -633,13 +657,13 @@ the issues tracker on github, contact me ( -

                                                  The init file

                                                  +

                                                  The init file

                                                  If your DF folder contains a file named dfhack.init, its contents will be run every time you start DF. This allows setting up keybindings. An example file is provided as dfhack.init-example - you can tweak it and rename to dfhack.init if you want to use this functionality.

                                                  -

                                                  Setting keybindings

                                                  +

                                                  Setting keybindings

                                                  To set keybindings, use the built-in keybinding command. Like any other command it can be used at any time from the console, but it is also meaningful in the DFHack init file.

                                                  @@ -684,7 +708,7 @@ for context foo/bar/baz, possible matches are
                                                  -

                                                  Commands

                                                  +

                                                  Commands

                                                  DFHack command syntax consists of a command name, followed by arguments separated by whitespace. To include whitespace in an argument, quote it in double quotes. To include a double quote character, use \" inside double quotes.

                                                  @@ -706,13 +730,13 @@ The following two command lines are exactly equivalent:

                                                  to retrieve further help without having to look at this document. Alternatively, some accept a 'help'/'?' option on their command line.

                                                  -

                                                  Game progress

                                                  +

                                                  Game progress

                                                  -

                                                  die

                                                  +

                                                  die

                                                  Instantly kills DF without saving.

                                                  -

                                                  forcepause

                                                  +

                                                  forcepause

                                                  Forces DF to pause. This is useful when your FPS drops below 1 and you lose control of the game.

                                                  @@ -723,12 +747,12 @@ control of the game.

                                                  -

                                                  nopause

                                                  +

                                                  nopause

                                                  Disables pausing (both manual and automatic) with the exception of pause forced by 'reveal hell'. This is nice for digging under rivers.

                                                  -

                                                  fastdwarf

                                                  +

                                                  fastdwarf

                                                  Controls speedydwarf and teledwarf. Speedydwarf makes dwarves move quickly and perform tasks quickly. Teledwarf makes dwarves move instantaneously, but do jobs at the same speed.

                                                    @@ -745,29 +769,29 @@ that implements an even more aggressive version of speedydwarf.
                                                  -

                                                  Game interface

                                                  +

                                                  Game interface

                                                  -

                                                  follow

                                                  +

                                                  follow

                                                  Makes the game view follow the currently highlighted unit after you exit from current menu/cursor mode. Handy for watching dwarves running around. Deactivated by moving the view manually.

                                                  -

                                                  tidlers

                                                  +

                                                  tidlers

                                                  Toggle between all possible positions where the idlers count can be placed.

                                                  -

                                                  twaterlvl

                                                  +

                                                  twaterlvl

                                                  Toggle between displaying/not displaying liquid depth as numbers.

                                                  -

                                                  copystock

                                                  +

                                                  copystock

                                                  Copies the parameters of the currently highlighted stockpile to the custom stockpile settings and switches to custom stockpile placement mode, effectively allowing you to copy/paste stockpiles easily.

                                                  -

                                                  rename

                                                  +

                                                  rename

                                                  Allows renaming various things.

                                                  Options:

                                                  @@ -801,9 +825,9 @@ siege engine or an activity zone.
                                                  -

                                                  Adventure mode

                                                  +

                                                  Adventure mode

                                                  -

                                                  adv-bodyswap

                                                  +

                                                  adv-bodyswap

                                                  This allows taking control over your followers and other creatures in adventure mode. For example, you can make them pick up new arms and armor and equip them properly.

                                                  @@ -816,7 +840,7 @@ properly.

                                                  -

                                                  advtools

                                                  +

                                                  advtools

                                                  A package of different adventure mode tools (currently just one)

                                                  Usage:

                                                  @@ -839,9 +863,9 @@ on item type and being in shop.
                                                  -

                                                  Map modification

                                                  +

                                                  Map modification

                                                  -

                                                  changelayer

                                                  +

                                                  changelayer

                                                  Changes material of the geology layer under cursor to the specified inorganic RAW material. Can have impact on all surrounding regions, not only your embark! By default changing stone to soil and vice versa is not allowed. By default @@ -916,7 +940,7 @@ You did save your game, right?

                                                  -

                                                  changevein

                                                  +

                                                  changevein

                                                  Changes material of the vein under cursor to the specified inorganic RAW material. Only affects tiles within the current 16x16 block - for veins and large clusters, you will need to use this command multiple times.

                                                  @@ -929,7 +953,7 @@ large clusters, you will need to use this command multiple times.

                                                  -

                                                  changeitem

                                                  +

                                                  changeitem

                                                  Allows changing item material and base quality. By default the item currently selected in the UI will be changed (you can select items in the 'k' list or inside containers/inventory). By default change is only allowed if materials @@ -969,7 +993,7 @@ crafters/haulers.

                                                  -

                                                  colonies

                                                  +

                                                  colonies

                                                  Allows listing all the vermin colonies on the map and optionally turning them into honey bee colonies.

                                                  Options:

                                                  @@ -984,12 +1008,12 @@ crafters/haulers.

                                                  -

                                                  deramp (by zilpin)

                                                  +

                                                  deramp (by zilpin)

                                                  Removes all ramps designated for removal from the map. This is useful for replicating the old channel digging designation. It also removes any and all 'down ramps' that can remain after a cave-in (you don't have to designate anything for that to happen).

                                                  -

                                                  feature

                                                  +

                                                  feature

                                                  Enables management of map features.

                                                  • Discovering a magma feature (magma pool, volcano, magma sea, or curious @@ -1014,7 +1038,7 @@ that cavern to grow within your fortress.
                                                  -

                                                  liquids

                                                  +

                                                  liquids

                                                  Allows adding magma, water and obsidian to the game. It replaces the normal dfhack command line and can't be used from a hotkey. Settings will be remembered as long as dfhack runs. Intended for use in combination with the command @@ -1027,13 +1051,13 @@ temperatures (creating heat traps). You've been warned.

                                                  -

                                                  liquids-here

                                                  +

                                                  liquids-here

                                                  Run the liquid spawner with the current/last settings made in liquids (if no settings in liquids were made it paints a point of 7/7 magma by default).

                                                  Intended to be used as keybinding. Requires an active in-game cursor.

                                                  -

                                                  tiletypes

                                                  +

                                                  tiletypes

                                                  Can be used for painting map tiles and is an interactive command, much like liquids.

                                                  The tool works with two set of options and a brush. The brush determines which @@ -1094,27 +1118,27 @@ up.

                                                  For more details, see the 'help' command while using this.

                                                  -

                                                  tiletypes-commands

                                                  +

                                                  tiletypes-commands

                                                  Runs tiletypes commands, separated by ;. This makes it possible to change tiletypes modes from a hotkey.

                                                  -

                                                  tiletypes-here

                                                  +

                                                  tiletypes-here

                                                  Apply the current tiletypes options at the in-game cursor position, including the brush. Can be used from a hotkey.

                                                  -

                                                  tiletypes-here-point

                                                  +

                                                  tiletypes-here-point

                                                  Apply the current tiletypes options at the in-game cursor position to a single tile. Can be used from a hotkey.

                                                  -

                                                  tubefill

                                                  +

                                                  tubefill

                                                  Fills all the adamantine veins again. Veins that were empty will be filled in too, but might still trigger a demon invasion (this is a known bug).

                                                  -

                                                  extirpate

                                                  +

                                                  extirpate

                                                  A tool for getting rid of trees and shrubs. By default, it only kills a tree/shrub under the cursor. The plants are turned into ashes instantly.

                                                  Options:

                                                  @@ -1134,20 +1158,20 @@ a tree/shrub under the cursor. The plants are turned into ashes instantly.

                                                  -

                                                  grow

                                                  +

                                                  grow

                                                  Makes all saplings present on the map grow into trees (almost) instantly.

                                                  -

                                                  immolate

                                                  +

                                                  immolate

                                                  Very similar to extirpate, but additionally sets the plants on fire. The fires can and will spread ;)

                                                  -

                                                  regrass

                                                  +

                                                  regrass

                                                  Regrows grass. Not much to it ;)

                                                  -

                                                  weather

                                                  +

                                                  weather

                                                  Prints the current weather map by default.

                                                  Also lets you change the current weather to 'clear sky', 'rainy' or 'snowing'.

                                                  Options:

                                                  @@ -1168,9 +1192,9 @@ can and will spread ;)

                                                  -

                                                  Map inspection

                                                  +

                                                  Map inspection

                                                  -

                                                  cursecheck

                                                  +

                                                  cursecheck

                                                  Checks a single map tile or the whole map/world for cursed creatures (ghosts, vampires, necromancers, werebeasts, zombies).

                                                  With an active in-game cursor only the selected tile will be observed. @@ -1225,17 +1249,17 @@ of curses, for example.

                                                  -

                                                  flows

                                                  +

                                                  flows

                                                  A tool for checking how many tiles contain flowing liquids. If you suspect that your magma sea leaks into HFS, you can use this tool to be sure without revealing the map.

                                                  -

                                                  probe

                                                  +

                                                  probe

                                                  Can be used to determine tile properties like temperature.

                                                  -

                                                  prospect

                                                  +

                                                  prospect

                                                  Prints a big list of all the present minerals and plants. By default, only the visible part of the map is scanned.

                                                  Options:

                                                  @@ -1254,7 +1278,7 @@ the visible part of the map is scanned.

                                                  -

                                                  Pre-embark estimate

                                                  +

                                                  Pre-embark estimate

                                                  If prospect is called during the embark selection screen, it displays an estimate of layer stone availability.

                                                  @@ -1279,7 +1303,7 @@ that is actually present.

                                                  -

                                                  reveal

                                                  +

                                                  reveal

                                                  This reveals the map. By default, HFS will remain hidden so that the demons don't spawn. You can use 'reveal hell' to reveal everything. With hell revealed, you won't be able to unpause until you hide the map again. If you really want @@ -1288,34 +1312,34 @@ to unpause with hell revealed, use 'reveal demons'.

                                                  you move. When you use it this way, you don't need to run 'unreveal'.

                                                  -

                                                  unreveal

                                                  +

                                                  unreveal

                                                  Reverts the effects of 'reveal'.

                                                  -

                                                  revtoggle

                                                  +

                                                  revtoggle

                                                  Switches between 'reveal' and 'unreveal'.

                                                  -

                                                  revflood

                                                  +

                                                  revflood

                                                  This command will hide the whole map and then reveal all the tiles that have a path to the in-game cursor.

                                                  -

                                                  revforget

                                                  +

                                                  revforget

                                                  When you use reveal, it saves information about what was/wasn't visible before revealing everything. Unreveal uses this information to hide things again. This command throws away the information. For example, use in cases where you abandoned with the fort revealed and no longer want the data.

                                                  -

                                                  showmood

                                                  +

                                                  showmood

                                                  Shows all items needed for the currently active strange mood.

                                                  -

                                                  Designations

                                                  +

                                                  Designations

                                                  -

                                                  burrow

                                                  +

                                                  burrow

                                                  Miscellaneous burrow control. Allows manipulating burrows and automated burrow expansion while digging.

                                                  Options:

                                                  @@ -1363,17 +1387,17 @@ Digging 1-wide corridors with the miner inside the burrow is SLOW.
                                                  -

                                                  digv

                                                  +

                                                  digv

                                                  Designates a whole vein for digging. Requires an active in-game cursor placed over a vein tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles).

                                                  -

                                                  digvx

                                                  +

                                                  digvx

                                                  A permanent alias for 'digv x'.

                                                  -

                                                  digl

                                                  +

                                                  digl

                                                  Designates layer stone for digging. Requires an active in-game cursor placed over a layer stone tile. With the 'x' option, it will traverse z-levels (putting stairs between the same-material tiles). With the 'undo' option it @@ -1381,11 +1405,11 @@ will remove the dig designation instead (if you realize that digging out a 50 z-level deep layer was not such a good idea after all).

                                                  -

                                                  diglx

                                                  +

                                                  diglx

                                                  A permanent alias for 'digl x'.

                                                  -

                                                  digexp

                                                  +

                                                  digexp

                                                  This command can be used for exploratory mining.

                                                  See: http://df.magmawiki.com/index.php/DF2010:Exploratory_mining

                                                  There are two variables that can be set: pattern and filter.

                                                  @@ -1448,7 +1472,7 @@ z-level deep layer was not such a good idea after all).

                                                  -

                                                  digcircle

                                                  +

                                                  digcircle

                                                  A command for easy designation of filled and hollow circles. It has several types of options.

                                                  Shape:

                                                  @@ -1511,7 +1535,7 @@ repeats with the last selected parameters.

                                                -

                                                digtype

                                                +

                                                digtype

                                                For every tile on the map of the same vein type as the selected tile, this command designates it to have the same designation as the selected tile. If the selected tile has no designation, they will be dig designated. If an argument is given, the designation of the selected tile is ignored, and all appropriate tiles are set to the specified designation.

                                                Options:

                                                @@ -1539,7 +1563,7 @@ If an argument is given, the designation of the selected tile is ignored, and al
                                                -

                                                filltraffic

                                                +

                                                filltraffic

                                                Set traffic designations using flood-fill starting at the cursor.

                                                Traffic Type Codes:

                                                @@ -1578,7 +1602,7 @@ If an argument is given, the designation of the selected tile is ignored, and al 'filltraffic H' - When used in a room with doors, it will set traffic to HIGH in just that room.
                                                -

                                                alltraffic

                                                +

                                                alltraffic

                                                Set traffic designations for every single tile of the map (useful for resetting traffic designations).

                                                Traffic Type Codes:

                                                @@ -1602,7 +1626,7 @@ If an argument is given, the designation of the selected tile is ignored, and al 'alltraffic N' - Set traffic to 'normal' for all tiles.
                                                -

                                                getplants

                                                +

                                                getplants

                                                This tool allows plant gathering and tree cutting by RAW ID. Specify the types of trees to cut down and/or shrubs to gather by their plant names, separated by spaces.

                                                @@ -1629,9 +1653,9 @@ all valid plant IDs will be listed.

                                                -

                                                Cleanup and garbage disposal

                                                +

                                                Cleanup and garbage disposal

                                                -

                                                clean

                                                +

                                                clean

                                                Cleans all the splatter that get scattered all over the map, items and creatures. In an old fortress, this can significantly reduce FPS lag. It can also spoil your !!FUN!!, so think before you use it.

                                                @@ -1665,12 +1689,12 @@ also spoil your !!FUN!!, so think before you use it.

                                                -

                                                spotclean

                                                +

                                                spotclean

                                                Works like 'clean map snow mud', but only for the tile under the cursor. Ideal if you want to keep that bloody entrance 'clean map' would clean up.

                                                -

                                                autodump

                                                +

                                                autodump

                                                This utility lets you quickly move all items designated to be dumped. Items are instantly moved to the cursor position, the dump flag is unset, and the forbid flag is set, as if it had been dumped normally. @@ -1697,17 +1721,17 @@ Be aware that any active dump item tasks still point at the item.

                                                -

                                                autodump-destroy-here

                                                +

                                                autodump-destroy-here

                                                Destroy items marked for dumping under cursor. Identical to autodump destroy-here, but intended for use as keybinding.

                                                -

                                                autodump-destroy-item

                                                +

                                                autodump-destroy-item

                                                Destroy the selected item. The item may be selected in the 'k' list, or inside a container. If called again before the game is resumed, cancels destroy.

                                                -

                                                cleanowned

                                                +

                                                cleanowned

                                                Confiscates items owned by dwarfs. By default, owned food on the floor and rotten items are confistacted and dumped.

                                                Options:

                                                @@ -1741,13 +1765,13 @@ worn items with 'X' damage and above.
                                                -

                                                Bugfixes

                                                +

                                                Bugfixes

                                                -

                                                drybuckets

                                                +

                                                drybuckets

                                                This utility removes water from all buckets in your fortress, allowing them to be safely used for making lye.

                                                -

                                                fixdiplomats

                                                +

                                                fixdiplomats

                                                Up to version 0.31.12, Elves only sent Diplomats to your fortress to propose tree cutting quotas due to a bug; once that bug was fixed, Elves stopped caring about excess tree cutting. This command adds a Diplomat position to all Elven @@ -1756,19 +1780,19 @@ to violate them and potentially start wars) in case you haven't already modified your raws accordingly.

                                                -

                                                fixmerchants

                                                +

                                                fixmerchants

                                                This command adds the Guild Representative position to all Human civilizations, allowing them to make trade agreements (just as they did back in 0.28.181.40d and earlier) in case you haven't already modified your raws accordingly.

                                                -

                                                fixveins

                                                +

                                                fixveins

                                                Removes invalid references to mineral inclusions and restores missing ones. Use this if you broke your embark with tools like tiletypes, or if you accidentally placed a construction on top of a valuable mineral floor.

                                                -

                                                tweak

                                                +

                                                tweak

                                                Contains various tweaks for minor bugs.

                                                One-shot subcommands:

                                                @@ -1855,7 +1879,7 @@ to make them stand out more in the list.
                                                -

                                                fix-armory

                                                +

                                                fix-armory

                                                Enables a fix for storage of squad equipment in barracks.

                                                Specifically, it prevents your haulers from moving that equipment to stockpiles, and instead queues jobs to store it on weapon racks, @@ -1908,9 +1932,9 @@ these rules is intended by Toady; the rest are invented by this plugin.

                                                -

                                                Mode switch and reclaim

                                                +

                                                Mode switch and reclaim

                                                -

                                                lair

                                                +

                                                lair

                                                This command allows you to mark the map as 'monster lair', preventing item scatter on abandon. When invoked as 'lair reset', it does the opposite.

                                                Unlike reveal, this command doesn't save the information about tiles - you @@ -1930,7 +1954,7 @@ won't be able to restore state of real monster lairs using 'lair reset'.

                                                -

                                                mode

                                                +

                                                mode

                                                This command lets you see and change the game mode directly. Not all combinations are good for every situation and most of them will produce undesirable results. There are a few good ones though.

                                                @@ -1950,9 +1974,9 @@ You just created a returnable mountain home and gained an adventurer.

                                                -

                                                Visualizer and data export

                                                +

                                                Visualizer and data export

                                                -

                                                ssense / stonesense

                                                +

                                                ssense / stonesense

                                                An isometric visualizer that runs in a second window. This requires working graphics acceleration and at least a dual core CPU (otherwise it will slow down DF).

                                                @@ -1965,19 +1989,19 @@ thread: http://df.magmawiki.com/index.php/Utility:Stonesense/Content_repository

                                                -

                                                mapexport

                                                +

                                                mapexport

                                                Export the current loaded map as a file. This will be eventually usable with visualizers.

                                                -

                                                dwarfexport

                                                +

                                                dwarfexport

                                                Export dwarves to RuneSmith-compatible XML.

                                                -

                                                Job management

                                                +

                                                Job management

                                                -

                                                job

                                                +

                                                job

                                                Command for general job query and manipulation.

                                                Options:
                                                @@ -1996,7 +2020,7 @@ in a workshop, or the unit/jobs screen.
                                                -

                                                job-material

                                                +

                                                job-material

                                                Alter the material of the selected job.

                                                Invoked as:

                                                @@ -2014,7 +2038,7 @@ over the first available choice with the matching material.
                                              • -

                                                job-duplicate

                                                +

                                                job-duplicate

                                                Duplicate the selected job in a workshop:
                                                  @@ -2025,7 +2049,7 @@ instantly duplicates the job.
                                                -

                                                workflow

                                                +

                                                workflow

                                                Manage control of repeat jobs.

                                                Usage:

                                                @@ -2042,14 +2066,22 @@ Otherwise, enables or disables any of the following options:

                                                List workflow-controlled jobs (if in a workshop, filtered by it).
                                                workflow list
                                                List active constraints, and their job counts.
                                                -
                                                workflow count <constraint-spec> <cnt-limit> [cnt-gap], workflow amount <constraint-spec> <cnt-limit> [cnt-gap]
                                                -
                                                Set a constraint. The first form counts each stack as only 1 item.
                                                +
                                                workflow list-commands
                                                +
                                                List active constraints as workflow commands that re-create them; +this list can be copied to a file, and then reloaded using the +script built-in command.
                                                +
                                                workflow count <constraint-spec> <cnt-limit> [cnt-gap]
                                                +
                                                Set a constraint, counting every stack as 1 item.
                                                +
                                                workflow amount <constraint-spec> <cnt-limit> [cnt-gap]
                                                +
                                                Set a constraint, counting all items within stacks.
                                                workflow unlimit <constraint-spec>
                                                Delete a constraint.
                                                +
                                                workflow unlimit-all
                                                +
                                                Delete all constraints.
                                                -

                                                Function

                                                +

                                                Function

                                                When the plugin is enabled, it protects all repeat jobs from removal. If they do disappear due to any cause, they are immediately re-added to their workshop and suspended.

                                                @@ -2061,8 +2093,37 @@ the frequency of jobs being toggled.

                                                Check out the gui/workflow script below for a simple front-end integrated in the game UI.

                                                +
                                                +

                                                Constraint format

                                                +

                                                The contstraint spec consists of 4 parts, separated with '/' characters:

                                                +
                                                +ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,<quality>]
                                                +
                                                +

                                                The first part is mandatory and specifies the item type and subtype, +using the raw tokens for items, in the same syntax you would e.g. use +for a custom reaction input. See this list for more info: http://dwarffortresswiki.org/index.php/Item_token

                                                +

                                                The subsequent parts are optional:

                                                +
                                                  +
                                                • A generic material spec constrains the item material to one of +the hard-coded generic classes, which currently include:

                                                  +
                                                  +PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN
                                                  +METAL STONE SAND GLASS CLAY MILK
                                                  +
                                                  +
                                                • +
                                                • A specific material spec chooses the material exactly, using the +raw syntax for reaction input materials, e.g. INORGANIC:IRON, +although for convenience it also allows just IRON, or ACACIA:WOOD etc. +See this page for more details on the unabbreviated raw syntax:

                                                  +

                                                  http://dwarffortresswiki.org/index.php/Material_token

                                                  +
                                                • +
                                                • A comma-separated list of miscellaneous flags, which currently can +be used to ignore imported items or items below a certain quality.

                                                  +
                                                • +
                                                +
                                                -

                                                Constraint examples

                                                +

                                                Constraint examples

                                                Keep metal bolts within 900-1000, and wood/bone within 150-200.

                                                 workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100
                                                @@ -2084,6 +2145,10 @@ workflow count BOX/CLOTH,SILK,YARN 30
                                                 workflow count BAR//COAL 20
                                                 workflow count BAR//COPPER 30
                                                 
                                                +

                                                Produce 15-20 gold crafts.

                                                +
                                                +workflow count CRAFTS//GOLD 20
                                                +

                                                Collect 15-20 sand bags and clay boulders.

                                                 workflow count POWDER_MISC/SAND 20
                                                @@ -2091,25 +2156,31 @@ workflow count BOULDER/CLAY 20
                                                 

                                                Make sure there are always 80-100 units of dimple dye.

                                                -  workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
                                                -
                                                -In order for this to work, you have to set the material of the PLANT input
                                                +workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20
                                                +
                                                +
                                                +

                                                Note

                                                +

                                                In order for this to work, you have to set the material of the PLANT input on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material' -command. +command. Otherwise the plugin won't be able to deduce the output material.

                                                +
                                                +

                                                Maintain 10-100 locally-made crafts of exceptional quality.

                                                +
                                                +workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90
                                                 
                                                -

                                                Fortress activity management

                                                +

                                                Fortress activity management

                                                -

                                                seedwatch

                                                +

                                                seedwatch

                                                Tool for turning cooking of seeds and plants on/off depending on how much you have of them.

                                                See 'seedwatch help' for detailed description.

                                                -

                                                zone

                                                +

                                                zone

                                                Helps a bit with managing activity zones (pens, pastures and pits) and cages.

                                                Options:

                                                @@ -2208,7 +2279,7 @@ for war/hunt). Negatable.
                                                -

                                                Usage with single units

                                                +

                                                Usage with single units

                                                One convenient way to use the zone tool is to bind the command 'zone assign' to a hotkey, maybe also the command 'zone set'. Place the in-game cursor over a pen/pasture or pit, use 'zone set' to mark it. Then you can select units @@ -2217,7 +2288,7 @@ and use 'zone assign' to assign them to their new home. Allows pitting your own dwarves, by the way.

                                                -

                                                Usage with filters

                                                +

                                                Usage with filters

                                                All filters can be used together with the 'assign' command.

                                                Restrictions: It's not possible to assign units who are inside built cages or chained because in most cases that won't be desirable anyways. @@ -2235,14 +2306,14 @@ are not properly added to your own stocks; slaughtering them should work).

                                                Most filters can be negated (e.g. 'not grazer' -> race is not a grazer).

                                                -

                                                Mass-renaming

                                                +

                                                Mass-renaming

                                                Using the 'nick' command you can set the same nickname for multiple units. If used without 'assign', 'all' or 'count' it will rename all units in the current default target zone. Combined with 'assign', 'all' or 'count' (and further optional filters) it will rename units matching the filter conditions.

                                                -

                                                Cage zones

                                                +

                                                Cage zones

                                                Using the 'tocages' command you can assign units to a set of cages, for example a room next to your butcher shop(s). They will be spread evenly among available cages to optimize hauling to and butchering from them. For this to work you need @@ -2253,7 +2324,7 @@ would make no sense, but can be used together with 'nick' or 'remnick' and all the usual filters.

                                                -

                                                Examples

                                                +

                                                Examples

                                                zone assign all own ALPACA minage 3 maxage 10
                                                Assign all own alpacas who are between 3 and 10 years old to the selected @@ -2279,7 +2350,7 @@ on the current default zone.
                                                -

                                                autonestbox

                                                +

                                                autonestbox

                                                Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless @@ -2308,7 +2379,7 @@ frames between runs.

                                                -

                                                autobutcher

                                                +

                                                autobutcher

                                                Assigns lifestock for slaughter once it reaches a specific count. Requires that you add the target race(s) to a watch list. Only tame units will be processed.

                                                Named units will be completely ignored (to protect specific animals from @@ -2416,7 +2487,7 @@ autobutcher.bat

                                                -

                                                autolabor

                                                +

                                                autolabor

                                                Automatically manage dwarf labors.

                                                When enabled, autolabor periodically checks your dwarves and enables or disables labors. It tries to keep as many dwarves as possible busy but @@ -2430,14 +2501,14 @@ while it is enabled.

                                                -

                                                Other

                                                +

                                                Other

                                                -

                                                catsplosion

                                                +

                                                catsplosion

                                                Makes cats just multiply. It is not a good idea to run this more than once or twice.

                                                -

                                                dfusion

                                                +

                                                dfusion

                                                This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin.

                                                See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15

                                                Confirmed working DFusion plugins:

                                                @@ -2459,7 +2530,7 @@ twice.

                                                -

                                                misery

                                                +

                                                misery

                                                When enabled, every new negative dwarven thought will be multiplied by a factor (2 by default).

                                                Usage:

                                                @@ -2483,7 +2554,7 @@ twice.

                                                -

                                                Scripts

                                                +

                                                Scripts

                                                Lua or ruby scripts placed in the hack/scripts/ directory are considered for execution as if they were native DFHack commands. They are listed at the end of the 'ls' command output.

                                                @@ -2492,7 +2563,7 @@ only be listed by ls if called as 'ls -a'. This is intended as a way to hide scripts that are obscure, developer-oriented, or should be used as keybindings.

                                                Some notable scripts:

                                                -

                                                fix/*

                                                +

                                                fix/*

                                                Scripts in this subdirectory fix various bugs and issues, some of them obscure.

                                                • fix/dead-units

                                                  @@ -2518,22 +2589,22 @@ caused by autodump bugs or other hacking mishaps.

                                                -

                                                gui/*

                                                +

                                                gui/*

                                                Scripts that implement dialogs inserted into the main game window are put in this directory.

                                                -

                                                quicksave

                                                +

                                                quicksave

                                                If called in dwarf mode, makes DF immediately auto-save the game by setting a flag normally used in seasonal auto-save.

                                                -

                                                setfps

                                                +

                                                setfps

                                                Run setfps <number> to set the FPS cap at runtime, in case you want to watch combat in slow motion or something :)

                                                -

                                                siren

                                                +

                                                siren

                                                Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about noise, tiredness and lack of protection. Also, the units with interrupted @@ -2541,7 +2612,7 @@ breaks will go on break again a lot sooner. The script is intended for emergencies, e.g. when a siege appears, and all your military is partying.

                                                -

                                                growcrops

                                                +

                                                growcrops

                                                Instantly grow seeds inside farming plots.

                                                With no argument, this command list the various seed types currently in use in your farming plots. @@ -2553,7 +2624,7 @@ growcrops plump 40

                                                -

                                                removebadthoughts

                                                +

                                                removebadthoughts

                                                This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                                                The script can target a single creature, when used with the him argument, @@ -2567,7 +2638,7 @@ but in the short term your dwarves will get much more joyful.

                                                quickly after you unpause.

                                                -

                                                slayrace

                                                +

                                                slayrace

                                                Kills any unit of a given race.

                                                With no argument, lists the available races.

                                                With the special argument him, targets only the selected creature.

                                                @@ -2593,7 +2664,7 @@ slayrace elve magma
                                                -

                                                magmasource

                                                +

                                                magmasource

                                                Create an infinite magma source on a tile.

                                                This script registers a map tile as a magma source, and every 12 game ticks that tile receives 1 new unit of flowing magma.

                                                @@ -2608,7 +2679,7 @@ To remove all placed sources, call magmasource stop

                                                With no argument, this command shows an help message and list existing sources.

                                                -

                                                digfort

                                                +

                                                digfort

                                                A script to designate an area for digging according to a plan in csv format.

                                                This script, inspired from quickfort, can designate an area for digging. Your plan should be stored in a .csv file like this:

                                                @@ -2626,7 +2697,7 @@ To skip a row in your design, use a single ;.<

                                                The script takes the plan filename, starting from the root df folder.

                                                -

                                                superdwarf

                                                +

                                                superdwarf

                                                Similar to fastdwarf, per-creature.

                                                To make any creature superfast, target it ingame using 'v' and:

                                                @@ -2636,17 +2707,17 @@ superdwarf add
                                                 

                                                This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                                                -

                                                drainaquifer

                                                +

                                                drainaquifer

                                                Remove all 'aquifer' tag from the map blocks. Irreversible.

                                                -

                                                deathcause

                                                +

                                                deathcause

                                                Focus a body part ingame, and this script will display the cause of death of the creature.

                                                -

                                                In-game interface tools

                                                +

                                                In-game interface tools

                                                These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                                                @@ -2657,7 +2728,7 @@ display the word "DFHack" on the screen somewhere while active.

                                                guideline because it arguably just fixes small usability bugs in the game UI.

                                                -

                                                Dwarf Manipulator

                                                +

                                                Dwarf Manipulator

                                                Implemented by the manipulator plugin. To activate, open the unit screen and press 'l'.

                                                This tool implements a Dwarf Therapist-like interface within the game UI. The @@ -2693,7 +2764,7 @@ cursor onto that cell instead of toggling it. directly to the main dwarf mode screen.

                                                -

                                                gui/liquids

                                                +

                                                gui/liquids

                                                To use, bind to a key and activate in the 'k' mode.

                                                While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

                                                -

                                                gui/mechanisms

                                                +

                                                gui/mechanisms

                                                To use, bind to a key and activate in the 'q' mode.

                                                Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

                                                @@ -2727,7 +2798,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

                                                -

                                                gui/rename

                                                +

                                                gui/rename

                                                Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

                                                  @@ -2743,14 +2814,14 @@ It is also possible to rename zones from the 'i' menu.

                                                  The building or unit options are automatically assumed when in relevant ui state.

                                                -

                                                gui/room-list

                                                +

                                                gui/room-list

                                                To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

                                                The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

                                                -

                                                gui/choose-weapons

                                                +

                                                gui/choose-weapons

                                                Bind to a key, and activate in the Equip->View/Customize page of the military screen.

                                                Depending on the cursor location, it rewrites all 'individual choice weapon' entries in the selected squad or position to use a specific weapon type matching the assigned @@ -2760,13 +2831,13 @@ only that entry, and does it even if it is not 'individual choice'.

                                                and may lead to inappropriate weapons being selected.

                                                -

                                                gui/guide-path

                                                +

                                                gui/guide-path

                                                Bind to a key, and activate in the Hauling menu with the cursor over a Guide order.

                                                The script displays the cached path that will be used by the order; the game computes it when the order is executed for the first time.

                                                -

                                                gui/workshop-job

                                                +

                                                gui/workshop-job

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                The script shows a list of the input reagents of the selected job, and allows changing them like the job item-type and job item-material commands.

                                                @@ -2794,7 +2865,7 @@ and then try to change the input item type, now it won't let you select plan you have to unset the material first.

                                                -

                                                gui/workflow

                                                +

                                                gui/workflow

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                This script provides a simple interface to constraints managed by the workflow plugin. When active, it displays a list of all constraints applicable to the @@ -2816,7 +2887,7 @@ as described in workflow documentation above. can be used for troubleshooting jobs that don't match the right constraints.

                                                -

                                                gui/assign-rack

                                                +

                                                gui/assign-rack

                                                Bind to a key, and activate when viewing a weapon rack in the 'q' mode.

                                                This script is part of a group of related fixes to make the armory storage work again. The existing issues are:

                                                @@ -2836,7 +2907,7 @@ the intended user.

                                                -

                                                Behavior Mods

                                                +

                                                Behavior Mods

                                                These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

                                                @@ -2847,20 +2918,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

                                                -

                                                Siege Engine

                                                +

                                                Siege Engine

                                                The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                                                -

                                                Configuration UI

                                                +

                                                Configuration UI

                                                The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                                                The main mode displays the current target, selected ammo item type, linked stockpiles and @@ -2881,7 +2952,7 @@ menu.

                                                -

                                                Power Meter

                                                +

                                                Power Meter

                                                The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                                                The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -2890,11 +2961,11 @@ key and activate after selecting Pressure Plate in the build menu.

                                                configuration page, but configures parameters relevant to the modded power meter building.

                                                -

                                                Steam Engine

                                                +

                                                Steam Engine

                                                The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2905,7 +2976,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                                                -

                                                Construction

                                                +

                                                Construction

                                                The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                                                @@ -2929,7 +3000,7 @@ short axles that can be built later than both of the engines.

                                                -

                                                Operation

                                                +

                                                Operation

                                                In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -2960,7 +3031,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                                                -

                                                Explosions

                                                +

                                                Explosions

                                                The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                                                During operation weak parts get gradually worn out, and @@ -2969,7 +3040,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                                                -

                                                Save files

                                                +

                                                Save files

                                                It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -2980,7 +3051,7 @@ being generated.

                                                -

                                                Add Spatter

                                                +

                                                Add Spatter

                                                This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/Readme.rst b/Readme.rst index d9021c7cb..7fbc42528 100644 --- a/Readme.rst +++ b/Readme.rst @@ -58,9 +58,35 @@ The stonesense plugin might require some additional libraries on Linux. If any of the plugins or dfhack itself refuses to load, check the stderr.log file created in your DF folder. +Getting started +=============== + +If DFHack is installed correctly, it will automatically pop up a console +window once DF is started as usual on windows. Linux and Mac OS X require +running the dfhack script from the terminal, and will use that terminal for +the console. + +**NOTE**: The dfhack-run executable is there for calling DFHack commands in +an already running DF+DFHack instance from external OS scripts and programs, +and is *not* the way how you use DFHack normally. + +DFHack has a lot of features, which can be accessed by typing commands in the +console, or by mapping them to keyboard shortcuts. Most of the newer and more +user-friendly tools are designed to be at least partially used via the latter +way. + +In order to set keybindings, you have to create a text configuration file +called ``dfhack.init``; the installation comes with an example version called +``dfhack.init-example``, which is fully functional, covers all of the recent +features and can be simply renamed to ``dfhack.init``. You are encouraged to look +through it to learn which features it makes available under which key combinations. + +For more information, refer to the rest of this document. + ============ Using DFHack ============ + DFHack basically extends what DF can do with something similar to the drop-down console found in Quake engine games. On Windows, this is a separate command line window. On linux, the terminal used to launch the dfhack script is taken over @@ -1258,10 +1284,18 @@ Usage: List workflow-controlled jobs (if in a workshop, filtered by it). ``workflow list`` List active constraints, and their job counts. - ``workflow count [cnt-gap], workflow amount [cnt-gap]`` - Set a constraint. The first form counts each stack as only 1 item. + ``workflow list-commands`` + List active constraints as workflow commands that re-create them; + this list can be copied to a file, and then reloaded using the + ``script`` built-in command. + ``workflow count [cnt-gap]`` + Set a constraint, counting every stack as 1 item. + ``workflow amount [cnt-gap]`` + Set a constraint, counting all items within stacks. ``workflow unlimit `` Delete a constraint. + ``workflow unlimit-all`` + Delete all constraints. Function ........ @@ -1279,6 +1313,34 @@ the frequency of jobs being toggled. Check out the ``gui/workflow`` script below for a simple front-end integrated in the game UI. +Constraint format +................. + +The contstraint spec consists of 4 parts, separated with '/' characters:: + + ITEM[:SUBTYPE]/[GENERIC_MAT,...]/[SPECIFIC_MAT:...]/[LOCAL,] + +The first part is mandatory and specifies the item type and subtype, +using the raw tokens for items, in the same syntax you would e.g. use +for a custom reaction input. See this list for more info: http://dwarffortresswiki.org/index.php/Item_token + +The subsequent parts are optional: + +- A generic material spec constrains the item material to one of + the hard-coded generic classes, which currently include:: + + PLANT WOOD CLOTH SILK LEATHER BONE SHELL SOAP TOOTH HORN PEARL YARN + METAL STONE SAND GLASS CLAY MILK + +- A specific material spec chooses the material exactly, using the + raw syntax for reaction input materials, e.g. INORGANIC:IRON, + although for convenience it also allows just IRON, or ACACIA:WOOD etc. + See this page for more details on the unabbreviated raw syntax: + + http://dwarffortresswiki.org/index.php/Material_token + +- A comma-separated list of miscellaneous flags, which currently can + be used to ignore imported items or items below a certain quality. Constraint examples ................... @@ -1304,10 +1366,15 @@ Make sure there are always 25-30 empty bins/barrels/bags. Make sure there are always 15-20 coal and 25-30 copper bars. :: - + workflow count BAR//COAL 20 workflow count BAR//COPPER 30 +Produce 15-20 gold crafts. +:: + + workflow count CRAFTS//GOLD 20 + Collect 15-20 sand bags and clay boulders. :: @@ -1319,9 +1386,16 @@ Make sure there are always 80-100 units of dimple dye. workflow amount POWDER_MISC//MUSHROOM_CUP_DIMPLE:MILL 100 20 +.. note:: + In order for this to work, you have to set the material of the PLANT input on the Mill Plants job to MUSHROOM_CUP_DIMPLE using the 'job item-material' - command. + command. Otherwise the plugin won't be able to deduce the output material. + +Maintain 10-100 locally-made crafts of exceptional quality. +:: + + workflow count CRAFTS///LOCAL,EXCEPTIONAL 100 90 Fortress activity management diff --git a/dfhack.init-example b/dfhack.init-example index 20048e39e..729747714 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -2,21 +2,32 @@ # Generic dwarfmode bindings # ############################## +# toggle the display of water level as 1-7 tiles keybinding add Ctrl-W twaterlvl # with cursor: + +# designate the whole vein for digging keybinding add Ctrl-V digv keybinding add Ctrl-Shift-V "digv x" + +# clean the selected tile of blood etc keybinding add Ctrl-C spotclean + +# destroy items designated for dump in the selected tile keybinding add Ctrl-Shift-K autodump-destroy-here -# any item: +# with an item selected: + +# destroy the selected item keybinding add Ctrl-K autodump-destroy-item +# scripts: + # quicksave, only in main dwarfmode screen and menu page keybinding add Ctrl-Alt-S@dwarfmode/Default quicksave -# gui/rename script +# gui/rename script - rename units and buildings keybinding add Ctrl-Shift-N gui/rename keybinding add Ctrl-Shift-T "gui/rename unit-profession" @@ -31,10 +42,10 @@ keybinding add Ctrl-Shift-B "adv-bodyswap force" # Context-specific bindings # ############################# -# q->stockpile; p +# q->stockpile; p - copy & paste stockpiles keybinding add Alt-P copystock -# q->workshop +# q->workshop - duplicate the selected job keybinding add Ctrl-D job-duplicate # materials: q->workshop; b->select items @@ -48,7 +59,7 @@ keybinding add Shift-O "job-material OBSIDIAN" keybinding add Shift-T "job-material ORTHOCLASE" keybinding add Shift-G "job-material GLASS_GREEN" -# sort units and items +# sort units and items in the on-screen list keybinding add Alt-Shift-N "sort-units name" "sort-items description" keybinding add Alt-Shift-R "sort-units arrival" keybinding add Alt-Shift-T "sort-units profession" "sort-items type material" @@ -60,7 +71,7 @@ keybinding add Ctrl-M@dwarfmode/QueryBuilding/Some gui/mechanisms # browse rooms of same owner keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list -# interface for the liquids plugin +# interface for the liquids plugin - spawn water/magma/obsidian keybinding add Alt-L@dwarfmode/LookAround gui/liquids # machine power sensitive pressure plate construction diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index c3dbe20d9..e3fb7b32e 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -266,9 +266,16 @@ function constraintToToken(cspec) error('invalid material: '..cspec.mat_type..':'..(cspec.mat_index or -1)) end end - local qpart + local qlist = {} + if cspec.is_local then + table.insert(qlist, "LOCAL") + end if cspec.quality and cspec.quality > 0 then - qpart = df.item_quality[cspec.quality] or error('invalid quality: '..cspec.quality) + table.insert(qlist, df.item_quality[cspec.quality] or error('invalid quality: '..cspec.quality)) + end + local qpart + if #qlist > 0 then + qpart = table.concat(qlist, ',') end if mask_part or mat_part or qpart then diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index c89d87333..04e3c13b0 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -100,6 +100,19 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector ]\n" + " The first part is mandatory and specifies the item type and subtype,\n" + " using the raw tokens for items, in the same syntax you would e.g. use\n" + " for a custom reaction input. The subsequent parts are optional:\n" + " - A generic material spec constrains the item material to one of\n" + " the hard-coded generic classes, like WOOD, METAL, YARN or MILK.\n" + " - A specific material spec chooses the material exactly, using the\n" + " raw syntax for reaction input materials, e.g. INORGANIC:IRON,\n" + " although for convenience it also allows just IRON, or ACACIA:WOOD.\n" + " - A comma-separated list of miscellaneous flags, which currently can\n" + " be used to ignore imported items or items below a certain quality.\n" "Constraint examples:\n" " workflow amount AMMO:ITEM_AMMO_BOLTS/METAL 1000 100\n" " workflow amount AMMO:ITEM_AMMO_BOLTS/WOOD,BONE 200 50\n" @@ -124,6 +137,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector , bool> TMaterialCache; struct ItemConstraint { PersistentDataItem config; + // Fixed key parsed into fields bool is_craft; ItemTypeInfo item; MaterialInfo material; df::dfhack_material_category mat_mask; + item_quality::item_quality min_quality; + bool is_local; + + // Tracking data int weight; std::vector jobs; - item_quality::item_quality min_quality; - int item_amount, item_count, item_inuse; bool request_suspend, request_resume; @@ -299,8 +317,9 @@ struct ItemConstraint { public: ItemConstraint() - : is_craft(false), weight(0), min_quality(item_quality::Ordinary),item_amount(0), - item_count(0), item_inuse(0), is_active(false), cant_resume_reported(false) + : is_craft(false), min_quality(item_quality::Ordinary), is_local(false), + weight(0), item_amount(0), item_count(0), item_inuse(0), + is_active(false), cant_resume_reported(false) {} int goalCount() { return config.ival(0); } @@ -673,7 +692,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str if (mat_mask.whole != 0) weight += 100; - + MaterialInfo material; std::string matstr = vector_get(tokens,2); if (!matstr.empty() && (!material.find(matstr) || !material.isValid())) { @@ -681,21 +700,6 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str return NULL; } - item_quality::item_quality minqual = item_quality::Ordinary; - std::string qualstr = vector_get(tokens, 3); - if(!qualstr.empty()) { - if(qualstr == "ordinary") minqual = item_quality::Ordinary; - else if(qualstr == "wellcrafted") minqual = item_quality::WellCrafted; - else if(qualstr == "finelycrafted") minqual = item_quality::FinelyCrafted; - else if(qualstr == "superior") minqual = item_quality::Superior; - else if(qualstr == "exceptional") minqual = item_quality::Exceptional; - else if(qualstr == "masterful") minqual = item_quality::Masterful; - else { - out.printerr("Cannot find quality: %s\nKnown qualities: ordinary, wellcrafted, finelycrafted, superior, exceptional, masterful\n", qualstr.c_str()); - return NULL; - } - } - if (material.type >= 0) weight += (material.index >= 0 ? 5000 : 1000); @@ -704,13 +708,52 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str return NULL; } + item_quality::item_quality minqual = item_quality::Ordinary; + bool is_local = false; + std::string qualstr = vector_get(tokens, 3); + + if(!qualstr.empty()) + { + std::vector qtokens; + split_string(&qtokens, qualstr, ","); + + for (size_t i = 0; i < qtokens.size(); i++) + { + auto token = toLower(qtokens[i]); + + if (token == "local") + is_local = true; + else + { + bool found = false; + FOR_ENUM_ITEMS(item_quality, qv) + { + if (toLower(ENUM_KEY_STR(item_quality, qv)) != token) + continue; + minqual = qv; + found = true; + } + + if (!found) + { + out.printerr("Cannot parse token: %s\n", token.c_str()); + return NULL; + } + } + } + } + + if (is_local || minqual > item_quality::Ordinary) + weight += 10; + for (size_t i = 0; i < constraints.size(); i++) { ItemConstraint *ct = constraints[i]; if (ct->is_craft == is_craft && ct->item == item && ct->material == material && ct->mat_mask.whole == mat_mask.whole && - ct->min_quality == minqual) + ct->min_quality == minqual && + ct->is_local == is_local) return ct; } @@ -720,6 +763,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str nct->material = material; nct->mat_mask = mat_mask; nct->min_quality = minqual; + nct->is_local = is_local; nct->weight = weight; if (cfg) @@ -1099,9 +1143,11 @@ static void map_job_items(color_ostream &out) (cv->item.subtype != -1 && cv->item.subtype != isubtype)) continue; } - if(item->getQuality() < cv->min_quality) { - continue; - } + + if (cv->is_local && item->flags.bits.foreign) + continue; + if (item->getQuality() < cv->min_quality) + continue; TMaterialCache::iterator it = cv->material_cache.find(matkey); @@ -1307,6 +1353,7 @@ static void push_constraint(lua_State *L, ItemConstraint *cv) Lua::SetField(L, cv->material.index, ctable, "mat_index"); Lua::SetField(L, (int)cv->min_quality, ctable, "min_quality"); + Lua::SetField(L, (bool)cv->is_local, ctable, "is_local"); // Constraint value diff --git a/scripts/gui/workflow.lua b/scripts/gui/workflow.lua index 366e3ec91..84540b5ca 100644 --- a/scripts/gui/workflow.lua +++ b/scripts/gui/workflow.lua @@ -191,8 +191,15 @@ function JobConstraints:initListChoices(clist, sel_token) order_pen = COLOR_BLUE end local itemstr = describe_item_type(cons) - if cons.min_quality > 0 then - itemstr = itemstr .. ' ('..df.item_quality[cons.min_quality]..')' + if cons.min_quality > 0 or cons.is_local then + local lst = {} + if cons.is_local then + table.insert(lst, 'local') + end + if cons.min_quality > 0 then + table.insert(lst, string.lower(df.item_quality[cons.min_quality])) + end + itemstr = itemstr .. ' ('..table.concat(lst,',')..')' end local matstr = describe_material(cons) local matflagstr = '' From f657c20a1da4b5d6ce0ae95d60c5f2921c68ebf9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 Nov 2012 15:49:01 +0400 Subject: [PATCH 62/79] Add an internal API for converting between file and memory offsets. --- Lua API.html | 8 +++++++ Lua API.rst | 10 +++++++++ library/LuaApi.cpp | 15 +++++++++++++ library/Process-darwin.cpp | 9 ++++++-- library/Process-linux.cpp | 9 ++++++-- library/Process-windows.cpp | 39 +++++++++++++++++++++++++++++++--- library/VersionInfoFactory.cpp | 4 ++-- library/include/MemAccess.h | 4 +++- 8 files changed, 88 insertions(+), 10 deletions(-) diff --git a/Lua API.html b/Lua API.html index 226afa98a..d2e0da1ef 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1763,9 +1763,17 @@ global environment, persistent between calls to the script.

                                              • dfhack.internal.getVTable(name)

                                                Returns the pre-extracted vtable address name, or nil.

                                              • +
                                              • dfhack.internal.getImageBase()

                                                +

                                                Returns the mmap base of the executable.

                                                +
                                              • dfhack.internal.getRebaseDelta()

                                                Returns the ASLR rebase offset of the DF executable.

                                              • +
                                              • dfhack.internal.adjustOffset(offset[,to_file])

                                                +

                                                Returns the re-aligned offset, or nil if invalid. +If to_file is true, the offset is adjusted from memory to file. +This function returns the original value everywhere except windows.

                                                +
                                              • dfhack.internal.getMemRanges()

                                                Returns a sequence of tables describing virtual memory ranges of the process.

                                              • diff --git a/Lua API.rst b/Lua API.rst index d06e3d2e6..f89750e88 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -1618,10 +1618,20 @@ and are only documented here for completeness: Returns the pre-extracted vtable address ``name``, or *nil*. +* ``dfhack.internal.getImageBase()`` + + Returns the mmap base of the executable. + * ``dfhack.internal.getRebaseDelta()`` Returns the ASLR rebase offset of the DF executable. +* ``dfhack.internal.adjustOffset(offset[,to_file])`` + + Returns the re-aligned offset, or *nil* if invalid. + If ``to_file`` is true, the offset is adjusted from memory to file. + This function returns the original value everywhere except windows. + * ``dfhack.internal.getMemRanges()`` Returns a sequence of tables describing virtual memory ranges of the process. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 0151ed404..3862530dd 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1718,9 +1718,11 @@ static void *checkaddr(lua_State *L, int idx, bool allow_null = false) return rv; } +static uint32_t getImageBase() { return Core::getInstance().p->getBase(); } static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); } static const LuaWrapper::FunctionReg dfhack_internal_module[] = { + WRAP(getImageBase), WRAP(getRebaseDelta), { NULL, NULL } }; @@ -1774,6 +1776,18 @@ static int internal_getVTable(lua_State *L) return 1; } +static int internal_adjustOffset(lua_State *L) +{ + lua_settop(L, 2); + int off = luaL_checkint(L, 1); + int rv = Core::getInstance().p->adjustOffset(off, lua_toboolean(L, 2)); + if (rv >= 0) + lua_pushinteger(L, rv); + else + lua_pushnil(L); + return 1; +} + static int internal_getMemRanges(lua_State *L) { std::vector ranges; @@ -1981,6 +1995,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "getAddress", internal_getAddress }, { "setAddress", internal_setAddress }, { "getVTable", internal_getVTable }, + { "adjustOffset", internal_adjustOffset }, { "getMemRanges", internal_getMemRanges }, { "patchMemory", internal_patchMemory }, { "patchBytes", internal_patchBytes }, diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp index c5e4e4b85..d081c8c5c 100644 --- a/library/Process-darwin.cpp +++ b/library/Process-darwin.cpp @@ -220,9 +220,14 @@ void Process::getMemRanges( vector & ranges ) }*/ } -uint32_t Process::getBase() +uintptr_t Process::getBase() { - return 0; + return 0x1000000; +} + +int Process::adjustOffset(int offset, bool /*to_file*/) +{ + return offset; } static int getdir (string dir, vector &files) diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp index f88279b3f..046b7696d 100644 --- a/library/Process-linux.cpp +++ b/library/Process-linux.cpp @@ -155,9 +155,14 @@ void Process::getMemRanges( vector & ranges ) fclose(mapFile); } -uint32_t Process::getBase() +uintptr_t Process::getBase() { - return 0; + return 0x8048000; +} + +int Process::adjustOffset(int offset, bool /*to_file*/) +{ + return offset; } static int getdir (string dir, vector &files) diff --git a/library/Process-windows.cpp b/library/Process-windows.cpp index 966468fb5..6f79236f9 100644 --- a/library/Process-windows.cpp +++ b/library/Process-windows.cpp @@ -160,7 +160,7 @@ Process::Process(VersionInfoFactory * factory) identified = true; // give the process a data model and memory layout fixed for the base of first module my_descriptor = new VersionInfo(*vinfo); - my_descriptor->rebaseTo((uint32_t)d->base); + my_descriptor->rebaseTo(getBase()); for(size_t i = 0; i < threads_ids.size();i++) { HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]); @@ -394,13 +394,46 @@ void Process::getMemRanges( vector & ranges ) } } -uint32_t Process::getBase() +uintptr_t Process::getBase() { if(d) - return (uint32_t) d->base; + return (uintptr_t) d->base; return 0x400000; } +int Process::adjustOffset(int offset, bool to_file) +{ + if (!d) + return -1; + + for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++) + { + auto §ion = d->sections[i]; + + if (to_file) + { + unsigned delta = offset - section.VirtualAddress; + if (delta >= section.Misc.VirtualSize) + continue; + if (!section.PointerToRawData || delta >= section.SizeOfRawData) + return -1; + return (int)(section.PointerToRawData + delta); + } + else + { + unsigned delta = offset - section.PointerToRawData; + if (!section.PointerToRawData || delta >= section.SizeOfRawData) + continue; + if (delta >= section.Misc.VirtualSize) + return -1; + return (int)(section.VirtualAddress + delta); + } + } + + return -1; +} + + string Process::doReadClassName (void * vptr) { char * rtti = readPtr((char *)vptr - 0x4); diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index e8c0561cd..7142233d8 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -103,13 +103,13 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) { mem->setOS(OS_LINUX); // this is wrong... I'm not going to do base image relocation on linux though. - mem->setBase(0x0); + mem->setBase(0x8048000); } else if(os == "darwin") { mem->setOS(OS_APPLE); // this is wrong... I'm not going to do base image relocation on linux though. - mem->setBase(0x0); + mem->setBase(0x1000000); } else { diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index 1b8e687b9..22f15eecf 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -275,11 +275,13 @@ namespace DFHack { return my_descriptor; }; - uint32_t getBase(); + uintptr_t getBase(); /// get the DF Process ID int getPID(); /// get the DF Process FilePath std::string getPath(); + /// Adjust between in-memory and in-file image offset + int adjustOffset(int offset, bool to_file = false); /// millisecond tick count, exactly as DF uses uint32_t getTickCount(); From 012d22fa4f8c6562ed9fc9e11b6059942fa5e8c8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 Nov 2012 17:24:13 +0400 Subject: [PATCH 63/79] Add a script for manipulating binary patches at runtime, and some patches. --- NEWS | 1 + Readme.html | 151 ++++++++++-------- Readme.rst | 11 ++ dfhack.init-example | 23 +++ library/CMakeLists.txt | 4 + patches/v0.34.11 SDL/armorstand-capacity.dif | 142 ++++++++++++++++ patches/v0.34.11 SDL/custom-reagent-size.dif | 91 +++++++++++ patches/v0.34.11 SDL/deconstruct-heapfall.dif | 61 +++++++ patches/v0.34.11 SDL/deconstruct-teleport.dif | 104 ++++++++++++ .../v0.34.11 SDL/hospital-overstocking.dif | 62 +++++++ patches/v0.34.11 SDL/training-ammo.dif | 83 ++++++++++ patches/v0.34.11 SDL/weaponrack-unassign.dif | 61 +++++++ .../v0.34.11 linux/armorstand-capacity.dif | 147 +++++++++++++++++ .../v0.34.11 linux/custom-reagent-size.dif | 40 +++++ .../v0.34.11 linux/deconstruct-heapfall.dif | 83 ++++++++++ .../v0.34.11 linux/deconstruct-teleport.dif | 139 ++++++++++++++++ .../v0.34.11 linux/hospital-overstocking.dif | 60 +++++++ patches/v0.34.11 linux/training-ammo.dif | 85 ++++++++++ .../v0.34.11 linux/weaponrack-unassign.dif | 45 ++++++ scripts/binpatch.lua | 117 ++++++++++++++ 20 files changed, 1440 insertions(+), 70 deletions(-) create mode 100644 patches/v0.34.11 SDL/armorstand-capacity.dif create mode 100644 patches/v0.34.11 SDL/custom-reagent-size.dif create mode 100644 patches/v0.34.11 SDL/deconstruct-heapfall.dif create mode 100644 patches/v0.34.11 SDL/deconstruct-teleport.dif create mode 100644 patches/v0.34.11 SDL/hospital-overstocking.dif create mode 100644 patches/v0.34.11 SDL/training-ammo.dif create mode 100644 patches/v0.34.11 SDL/weaponrack-unassign.dif create mode 100644 patches/v0.34.11 linux/armorstand-capacity.dif create mode 100644 patches/v0.34.11 linux/custom-reagent-size.dif create mode 100644 patches/v0.34.11 linux/deconstruct-heapfall.dif create mode 100644 patches/v0.34.11 linux/deconstruct-teleport.dif create mode 100644 patches/v0.34.11 linux/hospital-overstocking.dif create mode 100644 patches/v0.34.11 linux/training-ammo.dif create mode 100644 patches/v0.34.11 linux/weaponrack-unassign.dif create mode 100644 scripts/binpatch.lua diff --git a/NEWS b/NEWS index 51321be95..494a6c680 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ DFHack future - added a small stand-alone utility for applying and removing binary patches. - removebadthoughts: add --dry-run option New scripts: + - binpatch: the same as the stand-alone binpatch.exe, but works at runtime. - region-pops: displays animal populations of the region and allows tweaking them. New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. diff --git a/Readme.html b/Readme.html index 16f3aed5a..737963372 100644 --- a/Readme.html +++ b/Readme.html @@ -489,49 +489,50 @@ access DF memory and allow for easier development of new tools.

                                              • Scripts
                                              • -
                                              • In-game interface tools
                                              • +
                                                +

                                                binpatch

                                                +

                                                Checks, applies or removes binary patches directly in memory at runtime:

                                                +
                                                +binpatch check/apply/remove <patchname>
                                                +
                                                +

                                                If the name of the patch has no extension or directory separators, the +script uses hack/patches/<df-version>/<name>.dif, thus auto-selecting +the version appropriate for the currently loaded executable.

                                                +
                                                -

                                                quicksave

                                                +

                                                quicksave

                                                If called in dwarf mode, makes DF immediately auto-save the game by setting a flag normally used in seasonal auto-save.

                                                -

                                                setfps

                                                +

                                                setfps

                                                Run setfps <number> to set the FPS cap at runtime, in case you want to watch combat in slow motion or something :)

                                                -

                                                siren

                                                +

                                                siren

                                                Wakes up sleeping units, cancels breaks and stops parties either everywhere, or in the burrows given as arguments. In return, adds bad thoughts about noise, tiredness and lack of protection. Also, the units with interrupted @@ -2612,7 +2623,7 @@ breaks will go on break again a lot sooner. The script is intended for emergencies, e.g. when a siege appears, and all your military is partying.

                                                -

                                                growcrops

                                                +

                                                growcrops

                                                Instantly grow seeds inside farming plots.

                                                With no argument, this command list the various seed types currently in use in your farming plots. @@ -2624,7 +2635,7 @@ growcrops plump 40

                                                -

                                                removebadthoughts

                                                +

                                                removebadthoughts

                                                This script remove negative thoughts from your dwarves. Very useful against tantrum spirals.

                                                The script can target a single creature, when used with the him argument, @@ -2638,7 +2649,7 @@ but in the short term your dwarves will get much more joyful.

                                                quickly after you unpause.

                                                -

                                                slayrace

                                                +

                                                slayrace

                                                Kills any unit of a given race.

                                                With no argument, lists the available races.

                                                With the special argument him, targets only the selected creature.

                                                @@ -2664,7 +2675,7 @@ slayrace elve magma
                                                -

                                                magmasource

                                                +

                                                magmasource

                                                Create an infinite magma source on a tile.

                                                This script registers a map tile as a magma source, and every 12 game ticks that tile receives 1 new unit of flowing magma.

                                                @@ -2679,7 +2690,7 @@ To remove all placed sources, call magmasource stop

                                                With no argument, this command shows an help message and list existing sources.

                                                -

                                                digfort

                                                +

                                                digfort

                                                A script to designate an area for digging according to a plan in csv format.

                                                This script, inspired from quickfort, can designate an area for digging. Your plan should be stored in a .csv file like this:

                                                @@ -2697,7 +2708,7 @@ To skip a row in your design, use a single ;.<

                                                The script takes the plan filename, starting from the root df folder.

                                                -

                                                superdwarf

                                                +

                                                superdwarf

                                                Similar to fastdwarf, per-creature.

                                                To make any creature superfast, target it ingame using 'v' and:

                                                @@ -2707,17 +2718,17 @@ superdwarf add
                                                 

                                                This plugin also shortens the 'sleeping' and 'on break' periods of targets.

                                                -

                                                drainaquifer

                                                +

                                                drainaquifer

                                                Remove all 'aquifer' tag from the map blocks. Irreversible.

                                                -

                                                deathcause

                                                +

                                                deathcause

                                                Focus a body part ingame, and this script will display the cause of death of the creature.

                                                -

                                                In-game interface tools

                                                +

                                                In-game interface tools

                                                These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                                                @@ -2728,7 +2739,7 @@ display the word "DFHack" on the screen somewhere while active.

                                                guideline because it arguably just fixes small usability bugs in the game UI.

                                                -

                                                Dwarf Manipulator

                                                +

                                                Dwarf Manipulator

                                                Implemented by the manipulator plugin. To activate, open the unit screen and press 'l'.

                                                This tool implements a Dwarf Therapist-like interface within the game UI. The @@ -2764,7 +2775,7 @@ cursor onto that cell instead of toggling it. directly to the main dwarf mode screen.

                                                -

                                                gui/liquids

                                                +

                                                gui/liquids

                                                To use, bind to a key and activate in the 'k' mode.

                                                While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

                                                -

                                                gui/mechanisms

                                                +

                                                gui/mechanisms

                                                To use, bind to a key and activate in the 'q' mode.

                                                Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

                                                @@ -2798,7 +2809,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

                                                -

                                                gui/rename

                                                +

                                                gui/rename

                                                Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

                                                  @@ -2814,14 +2825,14 @@ It is also possible to rename zones from the 'i' menu.

                                                  The building or unit options are automatically assumed when in relevant ui state.

                                                -

                                                gui/room-list

                                                +

                                                gui/room-list

                                                To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

                                                The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

                                                -

                                                gui/choose-weapons

                                                +

                                                gui/choose-weapons

                                                Bind to a key, and activate in the Equip->View/Customize page of the military screen.

                                                Depending on the cursor location, it rewrites all 'individual choice weapon' entries in the selected squad or position to use a specific weapon type matching the assigned @@ -2831,13 +2842,13 @@ only that entry, and does it even if it is not 'individual choice'.

                                                and may lead to inappropriate weapons being selected.

                                                -

                                                gui/guide-path

                                                +

                                                gui/guide-path

                                                Bind to a key, and activate in the Hauling menu with the cursor over a Guide order.

                                                The script displays the cached path that will be used by the order; the game computes it when the order is executed for the first time.

                                                -

                                                gui/workshop-job

                                                +

                                                gui/workshop-job

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                The script shows a list of the input reagents of the selected job, and allows changing them like the job item-type and job item-material commands.

                                                @@ -2865,7 +2876,7 @@ and then try to change the input item type, now it won't let you select plan you have to unset the material first.

                                                -

                                                gui/workflow

                                                +

                                                gui/workflow

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                This script provides a simple interface to constraints managed by the workflow plugin. When active, it displays a list of all constraints applicable to the @@ -2887,7 +2898,7 @@ as described in workflow documentation above. can be used for troubleshooting jobs that don't match the right constraints.

                                                -

                                                gui/assign-rack

                                                +

                                                gui/assign-rack

                                                Bind to a key, and activate when viewing a weapon rack in the 'q' mode.

                                                This script is part of a group of related fixes to make the armory storage work again. The existing issues are:

                                                @@ -2907,7 +2918,7 @@ the intended user.

                                                -

                                                Behavior Mods

                                                +

                                                Behavior Mods

                                                These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

                                                @@ -2918,20 +2929,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

                                                -

                                                Siege Engine

                                                +

                                                Siege Engine

                                                The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                                                -

                                                Configuration UI

                                                +

                                                Configuration UI

                                                The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                                                The main mode displays the current target, selected ammo item type, linked stockpiles and @@ -2952,7 +2963,7 @@ menu.

                                                -

                                                Power Meter

                                                +

                                                Power Meter

                                                The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                                                The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -2961,11 +2972,11 @@ key and activate after selecting Pressure Plate in the build menu.

                                                configuration page, but configures parameters relevant to the modded power meter building.

                                                -

                                                Steam Engine

                                                +

                                                Steam Engine

                                                The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2976,7 +2987,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                                                -

                                                Construction

                                                +

                                                Construction

                                                The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                                                @@ -3000,7 +3011,7 @@ short axles that can be built later than both of the engines.

                                                -

                                                Operation

                                                +

                                                Operation

                                                In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -3031,7 +3042,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                                                -

                                                Explosions

                                                +

                                                Explosions

                                                The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                                                During operation weak parts get gradually worn out, and @@ -3040,7 +3051,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                                                -

                                                Save files

                                                +

                                                Save files

                                                It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -3051,7 +3062,7 @@ being generated.

                                                -

                                                Add Spatter

                                                +

                                                Add Spatter

                                                This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/Readme.rst b/Readme.rst index 7fbc42528..16665ab98 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1761,6 +1761,17 @@ gui/* Scripts that implement dialogs inserted into the main game window are put in this directory. +binpatch +======== + +Checks, applies or removes binary patches directly in memory at runtime:: + + binpatch check/apply/remove + +If the name of the patch has no extension or directory separators, the +script uses ``hack/patches//.dif``, thus auto-selecting +the version appropriate for the currently loaded executable. + quicksave ========= diff --git a/dfhack.init-example b/dfhack.init-example index 729747714..885849c33 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -132,3 +132,26 @@ tweak fast-trade tweak military-stable-assign # in same list, color units already assigned to squads in brown & green tweak military-color-assigned + +####################################################### +# Apply binary patches at runtime # +# # +# Commented out by default; enable the ones you want. # +####################################################### + +# Bug 5994 - items teleported when removing a construction +#binpatch apply deconstruct-teleport +#binpatch apply deconstruct-heapfall + +# Bug 4406 - hospital overstocking on all items +#binpatch apply hospital-overstocking + +# Bug 808 - custom reactions completely using up all of their reagents +#binpatch apply custom-reagent-size + +# Bug 4530 - marksdwarves not training when quiver full of combat-only ammo +#binpatch apply training-ammo + +# Bug 1445 - weapon racks broken, armor stand capacity too low +#binpatch apply weaponrack-unassign +#binpatch apply armorstand-capacity diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 6f33d5c8a..b141e9fa5 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -346,6 +346,10 @@ install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts PATTERN "*.rb" ) +install(DIRECTORY ${dfhack_SOURCE_DIR}/patches + DESTINATION ${DFHACK_DATA_DESTINATION} + FILES_MATCHING PATTERN "*.dif") + # Unused for so long that it's not even relevant now... if(BUILD_DEVEL) if(WIN32) diff --git a/patches/v0.34.11 SDL/armorstand-capacity.dif b/patches/v0.34.11 SDL/armorstand-capacity.dif new file mode 100644 index 000000000..e7d69a8c2 --- /dev/null +++ b/patches/v0.34.11 SDL/armorstand-capacity.dif @@ -0,0 +1,142 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 + +0x2ac6b +CC CC CC CC CC +66 39 E8 EB 53 + +.text:0042B86B loc_42B86B: +.text:0042B86B cmp ax, bp +.text:0042B86E jmp short loc_42B8C3 + +0x2ac7b +CC CC CC CC CC +E9 96 A2 00 00 + +.text:0042B87B loc_42B87B: +.text:0042B87B jmp loc_435B16 + +0x2acc3 +CC CC CC CC CC CC CC CC CC CC CC CC CC +75 0A 66 FF 4C 24 16 79 03 58 EB AC C3 + +.text:0042B8C3 loc_42B8C3: +.text:0042B8C3 jnz short locret_42B8CF +.text:0042B8C5 dec word ptr [esp+16h] ; 4+8+8+2 +.text:0042B8CA jns short locret_42B8CF +.text:0042B8CC pop eax +.text:0042B8CD jmp short loc_42B87B +.text:0042B8CF locret_42B8CF: +.text:0042B8CF retn + +0x2b2a1 +CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC +66 C7 44 24 0E 01 00 8B 90 44 01 00 00 C3 CC + +.text:0042BEA1 loc_42BEA1: +.text:0042BEA1 mov word ptr [esp+0Eh], 1 ; 4+8+2 +.text:0042BEA8 mov edx, [eax+144h] +.text:0042BEAE retn + +0x34d91 +8B 90 44 01 00 00 +E8 0B 65 FF FF 90 + +<<<< +.text:00435991 mov edx, [eax+144h] +==== +.text:00435991 call loc_42BEA1 +.text:00435996 nop +>>>> + +0x34e53 +0F 84 BD 00 00 00 +E8 6B 5E FF FF 90 + +<<<< +.text:00435A53 jz loc_435B16 +==== +.text:00435A53 call loc_42B8C3 +.text:00435A58 nop +>>>> + +0x34ef3 +66 3B C5 74 1E +E8 73 5D FF FF + +<<<< +.text:00435AF3 cmp ax, bp +.text:00435AF6 jz short loc_435B16 +==== +.text:00435AF3 call loc_42B86B +>>>> + + +basically: + ++ int allowed_count = 1; // to mean 2 + ... +- if (type(item) == new_type) ++ if (type(item) == new_type && --allowed_count < 0) + return false; + +to allow up to two items of the same type at the same time + + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +0002AC6B: CC 66 +0002AC6C: CC 39 +0002AC6D: CC E8 +0002AC6E: CC EB +0002AC6F: CC 53 +0002AC7B: CC E9 +0002AC7C: CC 96 +0002AC7D: CC A2 +0002AC7E: CC 00 +0002AC7F: CC 00 +0002ACC3: CC 75 +0002ACC4: CC 0A +0002ACC5: CC 66 +0002ACC6: CC FF +0002ACC7: CC 4C +0002ACC8: CC 24 +0002ACC9: CC 16 +0002ACCA: CC 79 +0002ACCB: CC 03 +0002ACCC: CC 58 +0002ACCD: CC EB +0002ACCE: CC AC +0002ACCF: CC C3 +0002B2A1: CC 66 +0002B2A2: CC C7 +0002B2A3: CC 44 +0002B2A4: CC 24 +0002B2A5: CC 0E +0002B2A6: CC 01 +0002B2A7: CC 00 +0002B2A8: CC 8B +0002B2A9: CC 90 +0002B2AA: CC 44 +0002B2AB: CC 01 +0002B2AC: CC 00 +0002B2AD: CC 00 +0002B2AE: CC C3 +00034D91: 8B E8 +00034D92: 90 0B +00034D93: 44 65 +00034D94: 01 FF +00034D95: 00 FF +00034D96: 00 90 +00034E53: 0F E8 +00034E54: 84 6B +00034E55: BD 5E +00034E56: 00 FF +00034E57: 00 FF +00034E58: 00 90 +00034EF3: 66 E8 +00034EF4: 3B 73 +00034EF5: C5 5D +00034EF6: 74 FF +00034EF7: 1E FF diff --git a/patches/v0.34.11 SDL/custom-reagent-size.dif b/patches/v0.34.11 SDL/custom-reagent-size.dif new file mode 100644 index 000000000..2227a48d0 --- /dev/null +++ b/patches/v0.34.11 SDL/custom-reagent-size.dif @@ -0,0 +1,91 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=808 + +Original code: + +.text:00916BCE mov edi, ebp +.text:00916BD0 call eax +.text:00916BD2 test eax, eax +.text:00916BD4 jnz short loc_916C1C + +.text:00916C0A mov edi, ebp + +.text:00916C14 mov edi, ebp + +Patch: + +0x2ac34: +CC CC CC CC CC CC CC CC CC CC CC CC +8B 7C 24 78 8B 3C B7 FF D0 EB 25 CC + +.text:0042B834 loc_42B834: +.text:0042B834 mov edi, [esp+78h] +.text:0042B838 mov edi, [edi+esi*4] +.text:0042B83B call eax +.text:0042B83D jmp short unk_42B864 + +0x2ac64 +CC CC CC CC CC CC CC CC CC CC CC CC +85 C0 E9 69 B3 4E 00 CC CC CC CC CC + +.text:0042B864 loc_42B864: +.text:0042B864 test eax, eax +.text:0042B866 jmp loc_916BD4 + +0x515fce +8B FD FF D0 85 C0 +E9 61 4C B1 FF 90 + +.text:00916BCE jmp loc_42B834 +.text:00916BD3 nop +.text:00916BD4 loc_916BD4: + +0x51600a +8B FD +90 90 + +.text:00916C0A nop +.text:00916C0B nop + +0x516014 +8B FD +90 90 + +.text:00916C14 nop +.text:00916C15 nop + + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +0002AC34: CC 8B +0002AC35: CC 7C +0002AC36: CC 24 +0002AC37: CC 78 +0002AC38: CC 8B +0002AC39: CC 3C +0002AC3A: CC B7 +0002AC3B: CC FF +0002AC3C: CC D0 +0002AC3D: CC EB +0002AC3E: CC 25 +0002AC64: CC 85 +0002AC65: CC C0 +0002AC66: CC E9 +0002AC67: CC 69 +0002AC68: CC B3 +0002AC69: CC 4E +0002AC6A: CC 00 +00515FCE: 8B E9 +00515FCF: FD 61 +00515FD0: FF 4C +00515FD1: D0 B1 +00515FD2: 85 FF +00515FD3: C0 90 +0051600A: 8B 90 +0051600B: FD 90 +00516014: 8B 90 +00516015: FD 90 diff --git a/patches/v0.34.11 SDL/deconstruct-heapfall.dif b/patches/v0.34.11 SDL/deconstruct-heapfall.dif new file mode 100644 index 000000000..05c0047d3 --- /dev/null +++ b/patches/v0.34.11 SDL/deconstruct-heapfall.dif @@ -0,0 +1,61 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994 + +Original code: + +.text:008629BD mov edi, [eax+38h] +.text:008629C0 mov eax, [eax+3Ch] +.text:008629C3 mov [esp+1Ch], eax +.text:008629C7 cmp edi, eax +.text:008629C9 jnb short loc_862A22 +.text:008629CB jmp short loc_8629D0 +.text:008629CD lea ecx, [ecx+0] +... +.text:00862A19 add edi, 4 +.text:00862A1C cmp edi, [esp+1Ch] +.text:00862A20 jb short loc_8629D0 + +Patch: + +0x461dbd +8B 78 38 8B 40 3C 89 44 24 1C 3B F8 +8B 78 3C 8B 40 38 89 44 24 1C 39 F8 + +.text:008629BD mov edi, [eax+3Ch] +.text:008629C0 mov eax, [eax+38h] +.text:008629C3 mov [esp+1Ch], eax +.text:008629C7 cmp eax, edi + +0x461dcb +EB 03 8D 49 00 +83 EF 04 90 90 + +.text:008629CB sub edi, 4 +.text:008629CE nop +.text:008629CF nop + +0x461e19 +83 C7 04 3B 7C 24 1C 72 AE +83 EF 04 3B 7C 24 1C 73 AE + +.text:00862A19 sub edi, 4 +.text:00862A1C cmp edi, [esp+1Ch] +.text:00862A20 jnb short loc_8629D0 + + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +00461DBF: 38 3C +00461DC2: 3C 38 +00461DC7: 3B 39 +00461DCB: EB 83 +00461DCC: 03 EF +00461DCD: 8D 04 +00461DCE: 49 90 +00461DCF: 00 90 +00461E1A: C7 EF +00461E20: 72 73 diff --git a/patches/v0.34.11 SDL/deconstruct-teleport.dif b/patches/v0.34.11 SDL/deconstruct-teleport.dif new file mode 100644 index 000000000..c6037e2c6 --- /dev/null +++ b/patches/v0.34.11 SDL/deconstruct-teleport.dif @@ -0,0 +1,104 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994 + +0x461de2 +F6 46 0C 01 74 31 +E9 0A 8E BC FF 90 + +.text:008629E2 jmp near ptr loc_42B7F1 ; << CAVE +.text:008629E7 nop +.text:008629E8 loc_8629E8: + +0x2abf1 +CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC +8B 4C 24 2C F6 46 0C 01 75 08 E9 19 72 43 00 + +.text:0042B7F1 loc_42B7F1: +.text:0042B7F1 mov ecx, [esp+2Ch] ; job +.text:0042B7F5 test byte ptr [esi+0Ch], 1 +.text:0042B7F9 jnz short near ptr loc_42B803 +.text:0042B7FB coord_test_jfail: +.text:0042B7FB jmp loc_862A19 + +0x2ac03 +CC CC CC CC CC CC CC CC CC CC CC CC CC +8B 41 10 3B 46 04 75 F0 EB 06 CC CC CC + +.text:0042B803 loc_42B803: +.text:0042B803 mov eax, [ecx+10h] ; job->pos.(x,y) +.text:0042B806 cmp eax, [esi+4] ; item->pos.(x,y) +.text:0042B809 jnz short coord_test_jfail +.text:0042B80B jmp short near ptr loc_42B813 + +0x2ac13 +CC CC CC CC CC CC CC CC CC CC CC CC CC +66 8B 41 14 66 3B 46 08 75 DE EB 06 CC + +text:0042B813 loc_42B813: +.text:0042B813 mov ax, [ecx+14h] ; job->pos.z +.text:0042B817 cmp ax, [esi+8] ; item->pos.z +.text:0042B81B jnz short coord_test_jfail +.text:0042B81D jmp short near ptr loc_42B825 + +0x2ac25 +CC CC CC CC CC CC CC CC CC CC CC +E9 BE 71 43 00 CC CC CC CC CC CC + +.text:0042B825 loc_42B825: +.text:0042B825 jmp loc_8629E8 + + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +0002ABF1: CC 8B +0002ABF2: CC 4C +0002ABF3: CC 24 +0002ABF4: CC 2C +0002ABF5: CC F6 +0002ABF6: CC 46 +0002ABF7: CC 0C +0002ABF8: CC 01 +0002ABF9: CC 75 +0002ABFA: CC 08 +0002ABFB: CC E9 +0002ABFC: CC 19 +0002ABFD: CC 72 +0002ABFE: CC 43 +0002ABFF: CC 00 +0002AC03: CC 8B +0002AC04: CC 41 +0002AC05: CC 10 +0002AC06: CC 3B +0002AC07: CC 46 +0002AC08: CC 04 +0002AC09: CC 75 +0002AC0A: CC F0 +0002AC0B: CC EB +0002AC0C: CC 06 +0002AC13: CC 66 +0002AC14: CC 8B +0002AC15: CC 41 +0002AC16: CC 14 +0002AC17: CC 66 +0002AC18: CC 3B +0002AC19: CC 46 +0002AC1A: CC 08 +0002AC1B: CC 75 +0002AC1C: CC DE +0002AC1D: CC EB +0002AC1E: CC 06 +0002AC25: CC E9 +0002AC26: CC BE +0002AC27: CC 71 +0002AC28: CC 43 +0002AC29: CC 00 +00461DE2: F6 E9 +00461DE3: 46 0A +00461DE4: 0C 8E +00461DE5: 01 BC +00461DE6: 74 FF +00461DE7: 31 90 diff --git a/patches/v0.34.11 SDL/hospital-overstocking.dif b/patches/v0.34.11 SDL/hospital-overstocking.dif new file mode 100644 index 000000000..2bc305e60 --- /dev/null +++ b/patches/v0.34.11 SDL/hospital-overstocking.dif @@ -0,0 +1,62 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406 + +1. Include store in hospital jobs when recomputing counters + +0x68a63 +0F 85 58 01 00 00 +90 90 90 90 90 90 + +<<<< +.text:00469663 jnz loc_4697C1 +==== +.text:00469663 nop +.text:00469664 nop +.text:00469665 nop +.text:00469666 nop +.text:00469667 nop +.text:00469668 nop +>>>> + +- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue; ++ // NOP + +This reference points to the containers, not the hospital civzone. +Since fixing this properly is too hard for a patch, just remove the +check. Most people have only one hospital anyway, and it is better +to err on the side of caution here. + + +2. Make the stockpiling code increment the right stock counters + +0x3dcbf9 +8B 0C 90 8B 81 80 00 00 00 +8B 3C 90 8B 87 80 00 00 00 + +<<<< +.text:007DD7F9 mov ecx, [eax+edx*4] +.text:007DD7FC mov eax, [ecx+80h] +==== +.text:007DD7F9 mov edi, [eax+edx*4] +.text:007DD7FC mov eax, [edi+80h] +>>>> + +- id = civzones[i]->children[child_idx[i]]->id ++ cur_civzone = civzones[i] // existing var from previous loop ++ id = cur_civzone->children[child_idx[i]]->id + +The reason being, later code uses that var (at this point containing +useless data) to increment counters and amounts in the hospital. + + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +00068A63: 0F 90 +00068A64: 85 90 +00068A65: 58 90 +00068A66: 01 90 +00068A67: 00 90 +00068A68: 00 90 +003DCBFA: 0C 3C +003DCBFD: 81 87 diff --git a/patches/v0.34.11 SDL/training-ammo.dif b/patches/v0.34.11 SDL/training-ammo.dif new file mode 100644 index 000000000..ea50d4221 --- /dev/null +++ b/patches/v0.34.11 SDL/training-ammo.dif @@ -0,0 +1,83 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530 + +0x2ac85 +CC CC CC CC CC CC CC CC CC CC CC +29 44 24 24 8B 9D 28 01 00 00 C3 + +.text:0042B885 loc_42B885: +.text:0042B885 sub [esp+24h], eax +.text:0042B889 mov ebx, [ebp+128h] +.text:0042B88F retn + +0x2ac94 +CC CC CC CC CC CC CC CC CC CC CC CC +89 C1 8B 00 FF 90 34 02 00 00 EB E5 + +.text:0042B894 loc_42B894: +.text:0042B894 mov ecx, eax +.text:0042B896 mov eax, [eax] +.text:0042B898 call dword ptr [eax+234h] +.text:0042B89E jmp short loc_42B885 + +0x6e28ff +29 44 24 20 +90 90 90 90 + +<<<< +.text:00AE34FF sub [esp+20h], eax +==== +.text:00AE34FF nop +.text:00AE3500 nop +.text:00AE3501 nop +.text:00AE3502 nop +>>>> + +0x6e2999 +8B 9D 28 01 00 00 +E8 F6 82 94 FF 90 + +<<<< +.text:00AE3599 mov ebx, [ebp+128h] +==== +.text:00AE3599 call loc_42B894 +.text:00AE359E nop +>>>> + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +0002AC85: CC 29 +0002AC86: CC 44 +0002AC87: CC 24 +0002AC88: CC 24 +0002AC89: CC 8B +0002AC8A: CC 9D +0002AC8B: CC 28 +0002AC8C: CC 01 +0002AC8D: CC 00 +0002AC8E: CC 00 +0002AC8F: CC C3 +0002AC94: CC 89 +0002AC95: CC C1 +0002AC96: CC 8B +0002AC97: CC 00 +0002AC98: CC FF +0002AC99: CC 90 +0002AC9A: CC 34 +0002AC9B: CC 02 +0002AC9C: CC 00 +0002AC9D: CC 00 +0002AC9E: CC EB +0002AC9F: CC E5 +006E28FF: 29 90 +006E2900: 44 90 +006E2901: 24 90 +006E2902: 20 90 +006E2999: 8B E8 +006E299A: 9D F6 +006E299B: 28 82 +006E299C: 01 94 +006E299D: 00 FF +006E299E: 00 90 + diff --git a/patches/v0.34.11 SDL/weaponrack-unassign.dif b/patches/v0.34.11 SDL/weaponrack-unassign.dif new file mode 100644 index 000000000..7760048ba --- /dev/null +++ b/patches/v0.34.11 SDL/weaponrack-unassign.dif @@ -0,0 +1,61 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 + +0x4c05c4 +8B 8C 24 80 00 00 00 +89 C1 90 90 90 90 90 + +<<<< +.text:008C11C4 mov ecx, [esp+98h+var_18] +==== +.text:008C11C4 mov ecx, eax +.text:008C11C6 nop +.text:008C11C7 nop +.text:008C11C8 nop +.text:008C11C9 nop +.text:008C11CA nop +>>>> + +0x4c06a1 +8B 8C 24 80 00 00 00 +89 C1 90 90 90 90 90 + +<<<< +.text:008C12A1 mov ecx, [esp+98h+var_18] +==== +.text:008C12A1 mov ecx, eax +.text:008C12A3 nop +.text:008C12A4 nop +.text:008C12A5 nop +.text:008C12A6 nop +.text:008C12A7 nop +>>>> + + +basically: + + b_squad_id = building->getSpecificSquad(); +- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4)) ++ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4)) + unassign(building); + +the reason being, some_other_squad_id contains irrelevant garbage at this point + + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf Fortress.exe +004C05C4: 8B 89 +004C05C5: 8C C1 +004C05C6: 24 90 +004C05C7: 80 90 +004C05C8: 00 90 +004C05C9: 00 90 +004C05CA: 00 90 +004C06A1: 8B 89 +004C06A2: 8C C1 +004C06A3: 24 90 +004C06A4: 80 90 +004C06A5: 00 90 +004C06A6: 00 90 +004C06A7: 00 90 diff --git a/patches/v0.34.11 linux/armorstand-capacity.dif b/patches/v0.34.11 linux/armorstand-capacity.dif new file mode 100644 index 000000000..36dbcf0a9 --- /dev/null +++ b/patches/v0.34.11 linux/armorstand-capacity.dif @@ -0,0 +1,147 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 + +0x9461 +90 90 90 90 90 90 90 90 90 90 90 90 90 90 +C7 44 24 18 01 00 00 00 FF A0 44 01 00 00 + +.text:08051461 sub_8051461 proc near +.text:08051461 mov dword ptr [esp+18h], 1 +.text:08051469 jmp dword ptr [eax+144h] +.text:08051469 sub_8051461 endp + +0x9548 +90 90 90 90 90 90 90 90 +FF 4C 24 14 78 08 EB 0B + +.text:08051548 loc_8051548: +.text:08051548 dec dword ptr [esp+14h] +.text:0805154C js short loc_8051556 +.text:0805154E jmp short loc_805155B + +0x9556 +90 90 90 90 90 90 90 90 90 90 +E9 F6 8C 05 00 E9 D0 8D 05 00 + +.text:08051556 loc_8051556: +.text:08051556 jmp loc_80AA251 +.text:0805155B loc_805155B: +.text:0805155B jmp loc_80AA330 + +0x9568 +90 90 90 90 90 90 90 90 +FF 4C 24 14 78 E8 EB 06 + +.text:08051568 loc_8051568: +.text:08051568 dec [esp+14h] +.text:0805156C js short loc_8051556 +.text:0805156E jmp short loc_8051576 + +0x9576 +90 90 90 90 90 +E9 4D 8E 05 00 + +.text:08051576 loc_8051576: +.text:08051576 jmp loc_80AA3C8 + +0x62243 +FF 90 44 01 00 00 +E8 19 72 FA FF 90 + +<<<< +.text:080AA243 call dword ptr [eax+144h] +==== +.text:080AA243 call sub_8051461 +.text:080AA248 nop +>>>> + +0x62369 +E9 E3 FE FF FF +E9 DA 71 FA FF + +<<<< +.text:080AA369 jmp loc_80AA251 +==== +.text:080AA369 jmp loc_8051548 +>>>> + +0x623f6 +E9 56 FE FF FF +E9 6D 71 FA FF + +<<<< +.text:080AA3F6 jmp loc_80AA251 +==== +.text:080AA3F6 jmp loc_8051568 +>>>> + +basically: + ++ int allowed_count = 1; // to mean 2 + ... +- if (type(item) == new_type) ++ if (type(item) == new_type && --allowed_count < 0) + return false; + +to allow up to two items of the same type at the same time + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +00009461: 90 C7 +00009462: 90 44 +00009463: 90 24 +00009464: 90 18 +00009465: 90 01 +00009466: 90 00 +00009467: 90 00 +00009468: 90 00 +00009469: 90 FF +0000946A: 90 A0 +0000946B: 90 44 +0000946C: 90 01 +0000946D: 90 00 +0000946E: 90 00 +00009548: 90 FF +00009549: 90 4C +0000954A: 90 24 +0000954B: 90 14 +0000954C: 90 78 +0000954D: 90 08 +0000954E: 90 EB +0000954F: 90 0B +00009556: 90 E9 +00009557: 90 F6 +00009558: 90 8C +00009559: 90 05 +0000955A: 90 00 +0000955B: 90 E9 +0000955C: 90 D0 +0000955D: 90 8D +0000955E: 90 05 +0000955F: 90 00 +00009568: 90 FF +00009569: 90 4C +0000956A: 90 24 +0000956B: 90 14 +0000956C: 90 78 +0000956D: 90 E8 +0000956E: 90 EB +0000956F: 90 06 +00009576: 90 E9 +00009577: 90 4D +00009578: 90 8E +00009579: 90 05 +0000957A: 90 00 +00062243: FF E8 +00062244: 90 19 +00062245: 44 72 +00062246: 01 FA +00062247: 00 FF +00062248: 00 90 +0006236A: E3 DA +0006236B: FE 71 +0006236C: FF FA +000623F7: 56 6D +000623F8: FE 71 +000623F9: FF FA diff --git a/patches/v0.34.11 linux/custom-reagent-size.dif b/patches/v0.34.11 linux/custom-reagent-size.dif new file mode 100644 index 000000000..d99269db1 --- /dev/null +++ b/patches/v0.34.11 linux/custom-reagent-size.dif @@ -0,0 +1,40 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=808 + +for (i = 0; i < num_items; i++) +{ + ridx = reagent_idx[i]; + sz = reagent_quantity[ridx]; // used quantity + if (sz <= 0) continue; + reag = reagent[ridx]; + if (reag->flags.PRESERVE_REAGENT) continue; + rsz = items[i]->getTotalDimension(); +<<<<<<<< + if (reag->flags3.ANY_RAW_MATERIAL) + rsz *= BASE_SIZE(items[i]->getType()); + if (items[i]->subtractDimension(rsz)) +======== + /* Not in patch, but necessary for full correctness: + if (reag->flags3.ANY_RAW_MATERIAL) + rsz /= BASE_SIZE(items[i]->getType()); + */ + if (reag->flags3.ANY_RAW_MATERIAL) + sz *= BASE_SIZE(items[i]->getType()); + if (items[i]->subtractDimension(sz)) +>>>>>>>> + destroy_item(items[i]); + reagent_quantity[ridx] -= rsz + if (reagent_quantity[ridx] < 0) + reagent_quantity[ridx] = 0; +} + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +0087F7EF: F8 D8 +0087F86F: F8 D8 +0087F9AD: C7 C3 +0087F9ED: C7 C3 diff --git a/patches/v0.34.11 linux/deconstruct-heapfall.dif b/patches/v0.34.11 linux/deconstruct-heapfall.dif new file mode 100644 index 000000000..294118ac5 --- /dev/null +++ b/patches/v0.34.11 linux/deconstruct-heapfall.dif @@ -0,0 +1,83 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994 + +Original code: + +.text:087AC378 cmp edx, eax +.text:087AC37A mov [esp+4Ch], eax +.text:087AC37E jnb loc_87A7034 +.text:087AC384 mov [esp+48h], edx +.text:087AC388 mov [esp+54h], ebx +... +.text:087AC440 add dword ptr [esp+48h], 4 +.text:087AC445 mov ebp, [esp+48h] +.text:087AC449 cmp [esp+4Ch], ebp +.text:087AC44D ja loc_87AC38C + +Patch: + +0x76437a +89 44 24 4C +89 54 24 4C + +.text:087AC37A mov [esp+4Ch], edx + +0x764384 +89 54 24 48 89 5C 24 54 +E8 8A 51 8A FF 90 90 90 + +.text:087AC384 call sub_8051513 +.text:087AC389 nop +.text:087AC38A nop +.text:087AC38B nop + +0x764440 +83 44 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 87 39 FF FF FF +83 6C 24 48 04 8B 6C 24 48 39 6C 24 4C 0F 86 39 FF FF FF + +.text:087AC440 sub dword ptr [esp+48h], 4 +.text:087AC445 mov ebp, [esp+48h] +.text:087AC449 cmp [esp+4Ch], ebp +.text:087AC44D jbe loc_87AC38C + +0x9513 +90 90 90 90 90 90 90 90 90 90 90 90 90 +83 E8 04 89 44 24 4C 89 5C 24 58 C3 90 + +.text:08051513 sub_8051513 proc near +.text:08051513 sub eax, 4 +.text:08051516 mov [esp+4Ch], eax ; 48h +.text:0805151A mov [esp+58h], ebx ; 54h +.text:0805151E retn +.text:0805151E sub_8051513 endp + + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +00009513: 90 83 +00009514: 90 E8 +00009515: 90 04 +00009516: 90 89 +00009517: 90 44 +00009518: 90 24 +00009519: 90 4C +0000951A: 90 89 +0000951B: 90 5C +0000951C: 90 24 +0000951D: 90 58 +0000951E: 90 C3 +0076437B: 44 54 +00764384: 89 E8 +00764385: 54 8A +00764386: 24 51 +00764387: 48 8A +00764388: 89 FF +00764389: 5C 90 +0076438A: 24 90 +0076438B: 54 90 +00764441: 44 6C +0076444E: 87 86 diff --git a/patches/v0.34.11 linux/deconstruct-teleport.dif b/patches/v0.34.11 linux/deconstruct-teleport.dif new file mode 100644 index 000000000..3b3212109 --- /dev/null +++ b/patches/v0.34.11 linux/deconstruct-teleport.dif @@ -0,0 +1,139 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=5994 + +0x7643f8 +F6 46 0C 01 74 42 +E9 B6 50 8A FF 90 + +.text:087AC3F8 jmp loc_80514B3 ; << CAVE +.text:087AC3FD nop +.text:087AC3FE loc_87AC3FE: + +0x94b3 +90 90 90 90 90 90 90 90 90 90 90 90 90 +F6 46 0C 01 75 0A E9 82 AF 75 00 90 90 + +.text:080514B3 loc_80514B3: +.text:080514B3 test byte ptr [esi+0Ch], 1 +.text:080514B7 jnz short loc_80514C3 +.text:080514B9 coord_test_jfail: +.text:080514B9 jmp loc_87AC440 + +0x94c3 +90 90 90 90 90 90 90 90 90 90 90 90 90 +8D 9C 24 60 03 00 00 0F BF 03 EB 07 90 + +.text:080514C3 loc_80514C3: +.text:080514C3 lea ebx, [esp+360h] +.text:080514CA movsx eax, word ptr [ebx] ; job_z +.text:080514CD jmp short loc_80514D6 + +0x94d6 +90 90 90 90 90 90 90 90 90 90 +66 3B 46 08 75 DD EB 05 90 90 + +.text:080514D6 loc_80514D6: +.text:080514D6 cmp ax, [esi+8] ; item->pos.z +.text:080514DA jnz short coord_test_jfail +.text:080514DC jmp short loc_80514E3 + +0x94e3 +90 90 90 90 90 90 90 90 90 90 90 90 90 +0F BF 43 10 66 3B 46 04 75 CC EB 04 90 + +.text:080514E3 loc_80514E3: +.text:080514E3 movsx eax, word ptr [ebx+10h] ; job_x +.text:080514E7 cmp ax, [esi+4] ; item->pos.x +.text:080514EB jnz short coord_test_jfail +.text:080514ED jmp short loc_80514F3 + +0x94f3 +90 90 90 90 90 90 90 90 90 90 90 90 90 +0F BF 43 20 66 3B 46 06 75 BC EB 04 90 + +.text:080514F3 loc_80514F3: +.text:080514F3 movsx eax, word ptr [ebx+20h] ; job_y +.text:080514F7 cmp ax, [esi+6] ; item->pos.y +.text:080514FB jnz short coord_test_jfail +.text:080514FD jmp short loc_8051503 + +0x9503 +90 90 90 90 90 90 90 90 90 90 90 90 90 +E9 F6 AE 75 00 90 90 90 90 90 90 90 90 + +.text:08051503 loc_8051503: +.text:08051503 jmp loc_87AC3FE + + +You can use this script to apply the generated patch below: +http://stalkr.net/files/ida/idadif.py + +----8<---- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +000094B3: 90 F6 +000094B4: 90 46 +000094B5: 90 0C +000094B6: 90 01 +000094B7: 90 75 +000094B8: 90 0A +000094B9: 90 E9 +000094BA: 90 82 +000094BB: 90 AF +000094BC: 90 75 +000094BD: 90 00 +000094C3: 90 8D +000094C4: 90 9C +000094C5: 90 24 +000094C6: 90 60 +000094C7: 90 03 +000094C8: 90 00 +000094C9: 90 00 +000094CA: 90 0F +000094CB: 90 BF +000094CC: 90 03 +000094CD: 90 EB +000094CE: 90 07 +000094D6: 90 66 +000094D7: 90 3B +000094D8: 90 46 +000094D9: 90 08 +000094DA: 90 75 +000094DB: 90 DD +000094DC: 90 EB +000094DD: 90 05 +000094E3: 90 0F +000094E4: 90 BF +000094E5: 90 43 +000094E6: 90 10 +000094E7: 90 66 +000094E8: 90 3B +000094E9: 90 46 +000094EA: 90 04 +000094EB: 90 75 +000094EC: 90 CC +000094ED: 90 EB +000094EE: 90 04 +000094F3: 90 0F +000094F4: 90 BF +000094F5: 90 43 +000094F6: 90 20 +000094F7: 90 66 +000094F8: 90 3B +000094F9: 90 46 +000094FA: 90 06 +000094FB: 90 75 +000094FC: 90 BC +000094FD: 90 EB +000094FE: 90 04 +00009503: 90 E9 +00009504: 90 F6 +00009505: 90 AE +00009506: 90 75 +00009507: 90 00 +007643F8: F6 E9 +007643F9: 46 B6 +007643FA: 0C 50 +007643FB: 01 8A +007643FC: 74 FF +007643FD: 42 90 diff --git a/patches/v0.34.11 linux/hospital-overstocking.dif b/patches/v0.34.11 linux/hospital-overstocking.dif new file mode 100644 index 000000000..73e5d2fac --- /dev/null +++ b/patches/v0.34.11 linux/hospital-overstocking.dif @@ -0,0 +1,60 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=4406 + +1. Include store in hospital jobs when recomputing counters + +0x746d7 +75 D7 +90 90 + +<<<< +.text:080BC6D7 jnz short loc_80BC6B0 +==== +.text:080BC6D7 nop +.text:080BC6D8 nop +>>>> + +- if (job->getBuildingRef(BUILDING_DESTINATION) != this) continue; ++ // NOP + +This reference points to the containers, not the hospital civzone. +Since fixing this properly is too hard for a patch, just remove the +check. Most people have only one hospital anyway, and it is better +to err on the side of caution here. + + +2. Make the stockpiling code increment the right stock counters + +0x67cb0e +0B 04 90 +8B 1C 90 + +0x67cb18 +8B 40 74 +8B 43 74 + +<<<< +.text:086C4B0E mov eax, [eax+edx*4] +.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4] +.text:086C4B18 mov eax, [eax+74h] +==== +.text:086C4B0E mov ebx, [eax+edx*4] +.text:086C4B11 mov edx, [esp+ecx*4+39Ch+var_2B4] +.text:086C4B18 mov eax, [ebx+74h] +>>>> + +- id = civzones[i]->children[child_idx[i]]->id ++ cur_civzone = civzones[i] // existing var from previous loop ++ id = cur_civzone->children[child_idx[i]]->id + +The reason being, later code uses that var (at this point containing +useless data) to increment counters and amounts in the hospital. + + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +000746D7: 75 90 +000746D8: D7 90 +0067CB0F: 04 1C +0067CB19: 40 43 diff --git a/patches/v0.34.11 linux/training-ammo.dif b/patches/v0.34.11 linux/training-ammo.dif new file mode 100644 index 000000000..9fbcabe78 --- /dev/null +++ b/patches/v0.34.11 linux/training-ammo.dif @@ -0,0 +1,85 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=4530 + +0x9508 + +90 90 90 90 90 90 90 90 +E9 13 76 9C 00 90 90 90 + +.text:08051508 loc_8051508: +.text:08051508 jmp sub_8A18B20 + +0x9523 + +90 90 90 90 90 90 90 90 90 90 90 90 90 +50 8B 03 53 FF 90 34 02 00 00 EB 09 90 + +.text:08051523 sub_8051523: +.text:08051523 push eax +.text:08051524 mov eax, [ebx] +.text:08051526 push ebx +.text:08051527 call dword ptr [eax+234h] +.text:0805152D jmp short loc_8051538 + +0x9538 + +90 90 90 90 90 90 90 90 +29 44 24 64 5B 58 EB C8 + +.text:08051538 loc_8051538: +.text:08051538 sub [esp+64h], eax +.text:0805153C pop ebx +.text:0805153D pop eax +.text:0805153E jmp short loc_8051508 + +0xa5cdd0 + +29 44 24 58 +90 90 90 90 + +.text:08AA4DD0 nop +.text:08AA4DD1 nop +.text:08AA4DD2 nop +.text:08AA4DD3 nop + +0xa5e2c3 + +E8 58 28 F7 FF +E8 5B B2 5A FF + +.text:08AA62C3 call sub_8051523 + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +00009508: 90 E9 +00009509: 90 13 +0000950A: 90 76 +0000950B: 90 9C +0000950C: 90 00 +00009523: 90 50 +00009524: 90 8B +00009525: 90 03 +00009526: 90 53 +00009527: 90 FF +00009529: 90 34 +0000952A: 90 02 +0000952B: 90 00 +0000952C: 90 00 +0000952D: 90 EB +0000952E: 90 09 +00009538: 90 29 +00009539: 90 44 +0000953A: 90 24 +0000953B: 90 64 +0000953C: 90 5B +0000953D: 90 58 +0000953E: 90 EB +0000953F: 90 C8 +00A5CDD0: 29 90 +00A5CDD1: 44 90 +00A5CDD2: 24 90 +00A5CDD3: 58 90 +00A5E2C4: 58 5B +00A5E2C5: 28 B2 +00A5E2C6: F7 5A diff --git a/patches/v0.34.11 linux/weaponrack-unassign.dif b/patches/v0.34.11 linux/weaponrack-unassign.dif new file mode 100644 index 000000000..721f235e8 --- /dev/null +++ b/patches/v0.34.11 linux/weaponrack-unassign.dif @@ -0,0 +1,45 @@ +http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 + +Fix use of uninitialized variables to stop auto-unassigning racks: + +0x7ee948 +8B 7C 24 3C +89 C7 90 90 + +.text:08836948 mov edi, eax +.text:0883694A nop +.text:0883694B nop + + +0x7eea2f +8B 7C 24 3C +89 C7 90 90 + +.text:08836A2F mov edi, eax +.text:08836A31 nop +.text:08836A32 nop + + +basically: + + b_squad_id = building->getSpecificSquad(); +- if (b_squad_id != squad->id || !building->canUse(some_squad_id, 4)) ++ if (b_squad_id != squad->id || !building->canUse(b_squad_id, 4)) + unassign(building); + +the reason being, some_other_squad_id contains irrelevant garbage at this point + + +---8<--- +This difference file is created by The Interactive Disassembler + +Dwarf_Fortress +007EE948: 8B 89 +007EE949: 7C C7 +007EE94A: 24 90 +007EE94B: 3C 90 +007EEA2F: 8B 89 +007EEA30: 7C C7 +007EEA31: 24 90 +007EEA32: 3C 90 + diff --git a/scripts/binpatch.lua b/scripts/binpatch.lua new file mode 100644 index 000000000..f0f14e929 --- /dev/null +++ b/scripts/binpatch.lua @@ -0,0 +1,117 @@ +-- Apply or remove binary patches at runtime. + +local utils = require('utils') + +function load_patch(name) + local filename = name + local auto = false + if not string.match(filename, '[./\\]') then + auto = true + filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif' + end + + local file, err = io.open(filename, 'r') + if not file then + if auto and string.match(err, ': No such file or directory') then + return nil, 'no patch '..name..' for '..dfhack.getDFVersion() + else + return nil, err + end + end + + local old_bytes = {} + local new_bytes = {} + + for line in file:lines() do + if string.match(line, '^%x+:') then + local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$') + if not offset then + file:close() + return nil, 'Could not parse: '..line + end + + offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16) + if oldv > 255 or newv > 255 then + file:close() + return nil, 'Invalid byte values: '..line + end + + old_bytes[offset] = oldv + new_bytes[offset] = newv + end + end + + return { name = name, old_bytes = old_bytes, new_bytes = new_bytes } +end + +function rebase_table(input) + local output = {} + local base = dfhack.internal.getImageBase() + for k,v in pairs(input) do + local offset = dfhack.internal.adjustOffset(k) + if not offset then + return nil, string.format('invalid offset: %x', k) + end + output[base + offset] = v + end + return output +end + +function rebase_patch(patch) + local nold, err = rebase_table(patch.old_bytes) + if not nold then return nil, err end + local nnew, err = rebase_table(patch.new_bytes) + if not nnew then return nil, err end + return { name = patch.name, old_bytes = nold, new_bytes = nnew } +end + +function run_command(cmd,name) + local patch, err = load_patch(name) + if not patch then + dfhack.printerr('Could not load: '..err) + return + end + + local rpatch, err = rebase_patch(patch) + if not rpatch then + dfhack.printerr(name..': '..err) + return + end + + if cmd == 'check' then + local old_ok, err, addr = dfhack.internal.patchBytes({}, rpatch.old_bytes) + if old_ok then + print(name..': patch is not applied.') + elseif dfhack.internal.patchBytes({}, rpatch.new_bytes) then + print(name..': patch is applied.') + else + dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + end + elseif cmd == 'apply' then + local ok, err, addr = dfhack.internal.patchBytes(rpatch.new_bytes, rpatch.old_bytes) + if ok then + print(name..': applied the patch.') + elseif dfhack.internal.patchBytes({}, rpatch.new_bytes) then + print(name..': patch is already applied.') + else + dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + end + elseif cmd == 'remove' then + local ok, err, addr = dfhack.internal.patchBytes(rpatch.old_bytes, rpatch.new_bytes) + if ok then + print(name..': removed the patch.') + elseif dfhack.internal.patchBytes({}, rpatch.old_bytes) then + print(name..': patch is already removed.') + else + dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + end + else + qerror('Invalid command: '..cmd) + end +end + +local cmd,name = ... +if not cmd or not name then + qerror('Usage: binpatch check/apply/remove ') +end +run_command(cmd, name) From 683da39636c6dd9c9a3d3d51061e166cbfec8ca7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 11 Nov 2012 17:24:25 +0400 Subject: [PATCH 64/79] Fix dfusion build on linux. --- plugins/Dfusion/dfusion.cpp | 2 +- plugins/Dfusion/include/OutFile.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index 4507f9a15..15bcfa7a8 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -41,7 +41,7 @@ static int loadObjectFile(lua_State* L) //Lua::Push(L,buf); lua_pushlightuserdata(L,buf); lua_setfield(L,table_pos,"data"); - OutFile::vSymbol& symbols=f.GetSymbols(); + const OutFile::vSymbol &symbols=f.GetSymbols(); lua_newtable(L); for(size_t i=0;i Date: Sun, 11 Nov 2012 17:19:37 +0200 Subject: [PATCH 65/79] Fixed error in dfusion and added some readme. --- NEWS | 8 +++++++- Readme.rst | 22 +++++++++++++++------- plugins/Dfusion/include/OutFile.h | 2 +- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 51321be95..ad340447e 100644 --- a/NEWS +++ b/NEWS @@ -12,11 +12,16 @@ DFHack future - removebadthoughts: add --dry-run option New scripts: - region-pops: displays animal populations of the region and allows tweaking them. + - lua: lua interpreter. + - dfusion: misc scripts with a text based menu. + - embark: lets you embark anywhere. New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. - gui/workflow: a front-end for the workflow plugin. - gui/assign-rack: works together with a binary patch to fix weapon racks. + - gui/gm-editor: an universal editor for lots of dfhack things. + - gui/companion-order: a adventure mode command interface for your companions. Workflow plugin: - properly considers minecarts assigned to routes busy. - code for deducing job outputs rewritten in lua for flexibility. @@ -29,7 +34,8 @@ DFHack future properly designated barracks be used again for storage of squad equipment. New Search plugin by falconne: Adds an incremental search function to the Stocks, Trading and Unit List screens. - + Dfusion plugin: + Reworked to make use of lua modules, now all the scripts can be used from other scripts. DFHack v0.34.11-r2 diff --git a/Readme.rst b/Readme.rst index d9021c7cb..668d8be50 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1611,19 +1611,17 @@ twice. dfusion ------- -This is the DFusion lua plugin system by warmist/darius, running as a DFHack plugin. +This is the DFusion lua plugin system by Warmist, running as a DFHack plugin. There are two parts to this plugin: an interactive script that shows a text based menu and lua modules. Some of the functionality of is intentionaly left out of the menu: + :Friendship: a binary plugin that allows multi race forts (to use make a script that imports plugins.dfusion.friendship and use Friendship:install{table} table should contain list of race names.) + :Embark: a binary plugin that allows multi race embark (to use make a script that imports plugins.dfusion.embark and use Embark:install{table} table should contain list of race names or list of pairs (race-name, caste_id)). -See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=69682.15 +See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=93317.0 -Confirmed working DFusion plugins: - -:simple_embark: allows changing the number of dwarves available on embark. .. note:: * Some of the DFusion plugins aren't completely ported yet. This can lead to crashes. - * This is currently working only on Windows. - * The game will be suspended while you're using dfusion. Don't panic when it doen't respond. + * The game will be suspended while you're using dfusion. Don't panic when it doesn't respond. misery ------ @@ -1836,6 +1834,16 @@ deathcause Focus a body part ingame, and this script will display the cause of death of the creature. +lua +=== +There are three ways to invoke this command: + 1. without any parameters - starts an interactive lua interpreter + 2. -f "filename" or --file "filename" - loads and runs the file indicated by filename + 3. -s ["filename"] or --save ["filename"] - loads and runs the file indicated by filename from save directory. If filename is not supplied it loads "dfhack.lua" + +embark +====== +Allows to embark anywhere. Currently windows only. ======================= In-game interface tools diff --git a/plugins/Dfusion/include/OutFile.h b/plugins/Dfusion/include/OutFile.h index 665a1e132..2ba9ecc80 100644 --- a/plugins/Dfusion/include/OutFile.h +++ b/plugins/Dfusion/include/OutFile.h @@ -105,7 +105,7 @@ public: void GetText(char *ptr); size_t GetTextSize(); void LoadSymbols(); - vSymbol GetSymbols(){LoadSymbols();return symbols;}; + const vSymbol& GetSymbols(){LoadSymbols();return symbols;}; void PrintSymbols(); void PrintRelocations(); protected: From 3eb852a43b6c1b9076084b4b89d2a78dce76f32b Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 Nov 2012 21:18:59 +0200 Subject: [PATCH 66/79] Added cheat commands to companion-order, (including rumrushers) --- plugins/lua/dfusion/tools.lua | 70 ++++++++++++++++++++++++++++++--- scripts/gui/companion-order.lua | 57 +++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua index e577a9418..86efe009c 100644 --- a/plugins/lua/dfusion/tools.lua +++ b/plugins/lua/dfusion/tools.lua @@ -150,13 +150,8 @@ function project(unit,trg) --TODO add to menu? end function empregnate(unit) if unit==nil then - unit=getSelectedUnit() - end - - if unit==nil then - unit=getCreatureAtPos(getxyz()) + unit=dfhack.gui.getSelectedUnit() end - if unit==nil then error("Failed to empregnate. Unit not selected/valid") end @@ -182,4 +177,67 @@ function empregnate(unit) unit.relations.pregnancy_mystery=1 end menu:add("Empregnate",empregnate) +function healunit(unit) + if unit==nil then + unit=dfhack.gui.getSelectedUnit() + end + + if unit==nil then + error("Failed to Heal unit. Unit not selected/valid") + end + + unit.body.wounds:resize(0) -- memory leak here :/ + unit.body.blood_count=unit.body.blood_max + --set flags for standing and grasping... + unit.status2.able_stand=4 + unit.status2.able_stand_impair=4 + unit.status2.able_grasp=4 + unit.status2.able_grasp_impair=4 + --should also set temperatures, and flags for breath etc... + unit.flags1.dead=false + unit.flags2.calculated_bodyparts=false + unit.flags2.calculated_nerves=false + unit.flags2.circulatory_spray=false + unit.flags2.vision_good=true + unit.flags2.vision_damaged=false + unit.flags2.vision_missing=false + unit.counters.winded=0 + unit.counters.unconscious=0 + for k,v in pairs(unit.body.components) do + for kk,vv in pairs(v) do + if k == 'body_part_status' then v[kk].whole = 0 else v[kk] = 0 end + end + end +end +menu:add("Heal unit",healunit) +function powerup(unit,labor_rating,military_rating,skills) + if unit==nil then + unit=dfhack.gui.getSelectedUnit() + end + if unit==nil then + error("Failed to power up unit. Unit not selected/valid") + end + + if unit.status.current_soul== nil then + error("Failed to power up unit. Unit has no soul") + end + local utils = require 'utils' + labor_rating = labor_rating or 15 + military_rating = military_rating or 70 + + skill =skill or { 0,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,41,42,43,44,45,46,47,48,49,54,55,57,58,59,60,61,62,63,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,95,96,97,98,99,100,101,102,103,104,105,109,110,111,112,113,114,115 } + local military = { 38,39,41,42,43,44,45,46,54,99,100,101,102,103,104,105 } + + for sk,sv in ipairs(skill) do + local new_rating = labor_rating + for _,v in ipairs(military) do + if v == sv then + local new_rating = military_rating + end + end + utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = sv, rating = new_rating, experience = (new_rating * 500) + (new_rating * (new_rating - 1)) * 50}, 'id') + end + +end +menu:add("Power up",powerup) return _ENV \ No newline at end of file diff --git a/scripts/gui/companion-order.lua b/scripts/gui/companion-order.lua index 6b6a79aa2..6a25787e6 100644 --- a/scripts/gui/companion-order.lua +++ b/scripts/gui/companion-order.lua @@ -1,7 +1,8 @@ local gui = require 'gui' local dlg = require 'gui.dialogs' - +local args={...} +local is_cheat=(#args>0 and args[1]=="-c") local cursor=xyz2pos(df.global.cursor.x,df.global.cursor.y,df.global.cursor.z) local permited_equips={} @@ -28,6 +29,13 @@ function CheckCursor(p) end return true end +function getxyz() -- this will return pointers x,y and z coordinates. + local x=df.global.cursor.x + local y=df.global.cursor.y + local z=df.global.cursor.z + return x,y,z -- return the coords +end + function GetCaste(race_id,caste_id) local race=df.creature_raw.find(race_id) return race.caste[caste_id] @@ -302,8 +310,39 @@ end}, end return true end}, +{name="Get in",f=function (unit_list,pos) + if not CheckCursor(pos) then + return false + end + adv=df.global.world.units.active[0] + item=getItemsAtPos(getxyz())[1] + print(item.id) + for k,v in pairs(unit_list) do + v.riding_item_id=item.id + local ref=df.general_ref_unit_riderst:new() + ref.unit_id=v.id + item.itemrefs:insert("#",ref) + end + return true +end}, +} +local cheats={ +{name="Patch up",f=function (unit_list) + local dft=require("plugins.dfusion.tools") + for k,v in pairs(unit_list) do + dft.healunit(v) + end + return true +end}, +{name="Power up",f=function (unit_list) + local dft=require("plugins.dfusion.tools") + for k,d in pairs(unit_list) do + dft.powerup(d) + end + return true +end}, + } -local cheats={} --[[ todo: add cheats...]]-- function getCompanions(unit) unit=unit or df.global.world.units.active[0] @@ -371,7 +410,14 @@ function CompanionUi:onInput(keys) self:dismiss() end end - --do order + if is_cheat then + idx=idx-#orders + if cheats[idx] and cheats[idx].f then + if cheats[idx].f(self:GetSelectedUnits(),cursor) then + self:dismiss() + end + end + end end end end @@ -393,6 +439,11 @@ function CompanionUi:onRenderBody( dc) for k,v in ipairs(orders) do dc:seek(w/2,k):string(string.char(k+char_A)..". "):string(v.name); end + if is_cheat then + for k,v in ipairs(cheats) do + dc:seek(w/2,k+#orders):string(string.char(k+#orders+char_A)..". "):string(v.name); + end + end end local screen=CompanionUi{unit_list=getCompanions()} screen:show() \ No newline at end of file From ce8ada4419727d20c710a911fd257970667c91ef Mon Sep 17 00:00:00 2001 From: Warmist Date: Sun, 11 Nov 2012 22:14:00 +0200 Subject: [PATCH 67/79] More bug fixing --- plugins/lua/dfusion/tools.lua | 4 +-- scripts/gui/companion-order.lua | 55 +++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua index 86efe009c..f1fdadf2b 100644 --- a/plugins/lua/dfusion/tools.lua +++ b/plugins/lua/dfusion/tools.lua @@ -94,8 +94,8 @@ function MakeFollow(unit,trgunit) trgunit=df.global.world.units.active[0] end unit.relations.group_leader_id=trgunit.id - local u_nem=getNemesis(unit) - local t_nem=getNemesis(trgunit) + local u_nem=dfhack.units.getNemesis(unit) + local t_nem=dfhack.units.getNemesis(trgunit) if u_nem then u_nem.group_leader_id=t_nem.id end diff --git a/scripts/gui/companion-order.lua b/scripts/gui/companion-order.lua index 6a25787e6..b9e3b8300 100644 --- a/scripts/gui/companion-order.lua +++ b/scripts/gui/companion-order.lua @@ -7,6 +7,7 @@ local cursor=xyz2pos(df.global.cursor.x,df.global.cursor.y,df.global.cursor.z) local permited_equips={} permited_equips[df.item_backpackst]="UPPERBODY" +permited_equips[df.item_quiverst]="UPPERBODY" permited_equips[df.item_flaskst]="UPPERBODY" permited_equips[df.item_armorst]="UPPERBODY" permited_equips[df.item_shoesst]="STANCE" @@ -14,7 +15,7 @@ permited_equips[df.item_glovesst]="GRASP" permited_equips[df.item_helmst]="HEAD" permited_equips[df.item_pantsst]="LOWERBODY" function DoesHaveSubtype(item) - if df.item_backpackst:is_instance(item) or df.item_flaskst:is_instance(item) then + if df.item_backpackst:is_instance(item) or df.item_flaskst:is_instance(item) or df.item_quiverst:is_instance(item) then return false end return true @@ -263,6 +264,26 @@ end}, end return true end}, +{name="unwield",f=function (unit_list) + + for k,v in pairs(unit_list) do + local wep_count=0 + for _,it in pairs(v.inventory) do + if it.mode==1 then + wep_count=wep_count+1 + end + end + for i=1,wep_count do + for _,it in pairs(v.inventory) do + if it.mode==1 then + dfhack.items.moveToGround(it.item,v.pos) + break + end + end + end + end + return true +end}, --[=[ {name="roam not working :<",f=function (unit_list,pos,dist) --does not work if not CheckCursor(pos) then @@ -310,21 +331,7 @@ end}, end return true end}, -{name="Get in",f=function (unit_list,pos) - if not CheckCursor(pos) then - return false - end - adv=df.global.world.units.active[0] - item=getItemsAtPos(getxyz())[1] - print(item.id) - for k,v in pairs(unit_list) do - v.riding_item_id=item.id - local ref=df.general_ref_unit_riderst:new() - ref.unit_id=v.id - item.itemrefs:insert("#",ref) - end - return true -end}, + } local cheats={ {name="Patch up",f=function (unit_list) @@ -341,7 +348,21 @@ end}, end return true end}, - +{name="get in",f=function (unit_list,pos) + if not CheckCursor(pos) then + return false + end + adv=df.global.world.units.active[0] + item=getItemsAtPos(getxyz())[1] + print(item.id) + for k,v in pairs(unit_list) do + v.riding_item_id=item.id + local ref=df.general_ref_unit_riderst:new() + ref.unit_id=v.id + item.itemrefs:insert("#",ref) + end + return true +end}, } --[[ todo: add cheats...]]-- function getCompanions(unit) From 6cf85b43185d5cd3bab4de368805cfea7f08b912 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 12 Nov 2012 12:26:31 +0400 Subject: [PATCH 68/79] Abstract the back-end from the binpatch script, and use in gui/assign-rack. --- Readme.html | 142 +++++++++++++++++++++--------------- Readme.rst | 11 ++- library/lua/binpatch.lua | 121 ++++++++++++++++++++++++++++++ scripts/binpatch.lua | 103 ++++---------------------- scripts/gui/assign-rack.lua | 26 +++---- 5 files changed, 239 insertions(+), 164 deletions(-) create mode 100644 library/lua/binpatch.lua diff --git a/Readme.html b/Readme.html index 737963372..b7ca94964 100644 --- a/Readme.html +++ b/Readme.html @@ -501,38 +501,40 @@ access DF memory and allow for easier development of new tools.

                                              • superdwarf
                                              • drainaquifer
                                              • deathcause
                                              • +
                                              • lua
                                              • +
                                              • embark
                                              • -
                                              • In-game interface tools
                                              • +
                                                +
                                                This is the DFusion lua plugin system by Warmist, running as a DFHack plugin. There are two parts to this plugin: an interactive script that shows a text based menu and lua modules. Some of the functionality of is intentionaly left out of the menu:
                                                +
                                                - + + +
                                                simple_embark:allows changing the number of dwarves available on embark.
                                                Friendship:a binary plugin that allows multi race forts (to use make a script that imports plugins.dfusion.friendship and use Friendship:install{table} table should contain list of race names.)
                                                Embark:a binary plugin that allows multi race embark (to use make a script that imports plugins.dfusion.embark and use Embark:install{table} table should contain list of race names or list of pairs (race-name, caste_id)).
                                                + + +

                                                See the bay12 thread for details: http://www.bay12forums.com/smf/index.php?topic=93317.0

                                                Note

                                                • Some of the DFusion plugins aren't completely ported yet. This can lead to crashes.
                                                • -
                                                • This is currently working only on Windows.
                                                • -
                                                • The game will be suspended while you're using dfusion. Don't panic when it doen't respond.
                                                • +
                                                • The game will be suspended while you're using dfusion. Don't panic when it doesn't respond.
                                                @@ -2726,9 +2732,25 @@ superdwarf add

                                                Focus a body part ingame, and this script will display the cause of death of the creature.

                                                +
                                                +

                                                lua

                                                +
                                                +
                                                There are three ways to invoke this command:
                                                +
                                                  +
                                                1. without any parameters - starts an interactive lua interpreter
                                                2. +
                                                3. -f "filename" or --file "filename" - loads and runs the file indicated by filename
                                                4. +
                                                5. -s ["filename"] or --save ["filename"] - loads and runs the file indicated by filename from save directory. If filename is not supplied it loads "dfhack.lua"
                                                6. +
                                                +
                                                +
                                                +
                                                +
                                                +

                                                embark

                                                +

                                                Allows to embark anywhere. Currently windows only.

                                                +
                                                -

                                                In-game interface tools

                                                +

                                                In-game interface tools

                                                These tools work by displaying dialogs or overlays in the game window, and are mostly implemented by lua scripts.

                                                @@ -2739,7 +2761,7 @@ display the word "DFHack" on the screen somewhere while active.

                                                guideline because it arguably just fixes small usability bugs in the game UI.

                                                -

                                                Dwarf Manipulator

                                                +

                                                Dwarf Manipulator

                                                Implemented by the manipulator plugin. To activate, open the unit screen and press 'l'.

                                                This tool implements a Dwarf Therapist-like interface within the game UI. The @@ -2775,7 +2797,7 @@ cursor onto that cell instead of toggling it. directly to the main dwarf mode screen.

                                                -

                                                gui/liquids

                                                +

                                                gui/liquids

                                                To use, bind to a key and activate in the 'k' mode.

                                                While active, use the suggested keys to switch the usual liquids parameters, and Enter to select the target area and apply changes.

                                                -

                                                gui/mechanisms

                                                +

                                                gui/mechanisms

                                                To use, bind to a key and activate in the 'q' mode.

                                                Lists mechanisms connected to the building, and their links. Navigating the list centers the view on the relevant linked buildings.

                                                @@ -2809,7 +2831,7 @@ focus on the current one. Shift-Enter has an effect equivalent to pressing Enter re-entering the mechanisms ui.

                                                -

                                                gui/rename

                                                +

                                                gui/rename

                                                Backed by the rename plugin, this script allows entering the desired name via a simple dialog in the game ui.

                                                  @@ -2825,14 +2847,14 @@ It is also possible to rename zones from the 'i' menu.

                                                  The building or unit options are automatically assumed when in relevant ui state.

                                                -

                                                gui/room-list

                                                +

                                                gui/room-list

                                                To use, bind to a key and activate in the 'q' mode, either immediately or after opening the assign owner page.

                                                The script lists other rooms owned by the same owner, or by the unit selected in the assign list, and allows unassigning them.

                                                -

                                                gui/choose-weapons

                                                +

                                                gui/choose-weapons

                                                Bind to a key, and activate in the Equip->View/Customize page of the military screen.

                                                Depending on the cursor location, it rewrites all 'individual choice weapon' entries in the selected squad or position to use a specific weapon type matching the assigned @@ -2842,13 +2864,13 @@ only that entry, and does it even if it is not 'individual choice'.

                                                and may lead to inappropriate weapons being selected.

                                                -

                                                gui/guide-path

                                                +

                                                gui/guide-path

                                                Bind to a key, and activate in the Hauling menu with the cursor over a Guide order.

                                                The script displays the cached path that will be used by the order; the game computes it when the order is executed for the first time.

                                                -

                                                gui/workshop-job

                                                +

                                                gui/workshop-job

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                The script shows a list of the input reagents of the selected job, and allows changing them like the job item-type and job item-material commands.

                                                @@ -2876,7 +2898,7 @@ and then try to change the input item type, now it won't let you select plan you have to unset the material first.

                                                -

                                                gui/workflow

                                                +

                                                gui/workflow

                                                Bind to a key, and activate with a job selected in a workshop in the 'q' mode.

                                                This script provides a simple interface to constraints managed by the workflow plugin. When active, it displays a list of all constraints applicable to the @@ -2898,7 +2920,7 @@ as described in workflow documentation above. can be used for troubleshooting jobs that don't match the right constraints.

                                                -

                                                gui/assign-rack

                                                +

                                                gui/assign-rack

                                                Bind to a key, and activate when viewing a weapon rack in the 'q' mode.

                                                This script is part of a group of related fixes to make the armory storage work again. The existing issues are:

                                                @@ -2907,7 +2929,9 @@ work again. The existing issues are:

                                                beds/boxes/armor stands and individual squad members, but nothing in the game does this. This issue is what this script addresses.
                                              • Even if assigned by the script, the game will unassign the racks again without a binary patch. -Check the comments for this bug to get it: +This patch is called weaponrack-unassign, and can be applied via +the binpatch program, or the matching script. See this for more info +about the bug: http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445
                                              • Haulers still take equpment stored in the armory away to the stockpiles, unless the fix-armory plugin above is used.
                                              • @@ -2918,7 +2942,7 @@ the intended user.

                                                -

                                                Behavior Mods

                                                +

                                                Behavior Mods

                                                These plugins, when activated via configuration UI or by detecting certain structures in RAWs, modify the game engine behavior concerning the target objects to add features not otherwise present.

                                                @@ -2929,20 +2953,20 @@ technical challenge, and do not represent any long-term plans to produce more similar modifications of the game.

                                                -

                                                Siege Engine

                                                +

                                                Siege Engine

                                                The siege-engine plugin enables siege engines to be linked to stockpiles, and aimed at an arbitrary rectangular area across Z levels, instead of the original four directions. Also, catapults can be ordered to load arbitrary objects, not just stones.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                Siege engines are a very interesting feature, but sadly almost useless in the current state because they haven't been updated since 2D and can only aim in four directions. This is an attempt to bring them more up to date until Toady has time to work on it. Actual improvements, e.g. like making siegers bring their own, are something only Toady can do.

                                                -

                                                Configuration UI

                                                +

                                                Configuration UI

                                                The configuration front-end to the plugin is implemented by the gui/siege-engine script. Bind it to a key and activate after selecting a siege engine in 'q' mode.

                                                The main mode displays the current target, selected ammo item type, linked stockpiles and @@ -2963,7 +2987,7 @@ menu.

                                                -

                                                Power Meter

                                                +

                                                Power Meter

                                                The power-meter plugin implements a modified pressure plate that detects power being supplied to gear boxes built in the four adjacent N/S/W/E tiles.

                                                The configuration front-end is implemented by the gui/power-meter script. Bind it to a @@ -2972,11 +2996,11 @@ key and activate after selecting Pressure Plate in the build menu.

                                                configuration page, but configures parameters relevant to the modded power meter building.

                                                -

                                                Steam Engine

                                                +

                                                Steam Engine

                                                The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.

                                                -

                                                Rationale

                                                +

                                                Rationale

                                                The vanilla game contains only water wheels and windmills as sources of power, but windmills give relatively little power, and water wheels require flowing water, which must either be a real river and thus immovable and @@ -2987,7 +3011,7 @@ it can be done just by combining existing features of the game engine in a new way with some glue code and a bit of custom logic.

                                                -

                                                Construction

                                                +

                                                Construction

                                                The workshop needs water as its input, which it takes via a passable floor tile below it, like usual magma workshops do. The magma version also needs magma.

                                                @@ -3011,7 +3035,7 @@ short axles that can be built later than both of the engines.

                                                -

                                                Operation

                                                +

                                                Operation

                                                In order to operate the engine, queue the Stoke Boiler job (optionally on repeat). A furnace operator will come, possibly bringing a bar of fuel, and perform it. As a result, a "boiling water" item will appear @@ -3042,7 +3066,7 @@ decrease it by further 4%, and also decrease the whole steam use rate by 10%.

                                                -

                                                Explosions

                                                +

                                                Explosions

                                                The engine must be constructed using barrel, pipe and piston from fire-safe, or in the magma version magma-safe metals.

                                                During operation weak parts get gradually worn out, and @@ -3051,7 +3075,7 @@ toppled during operation by a building destroyer, or a tantruming dwarf.

                                                -

                                                Save files

                                                +

                                                Save files

                                                It should be safe to load and view engine-using fortresses from a DF version without DFHack installed, except that in such case the engines won't work. However actually making modifications @@ -3062,7 +3086,7 @@ being generated.

                                                -

                                                Add Spatter

                                                +

                                                Add Spatter

                                                This plugin makes reactions with names starting with SPATTER_ADD_ produce contaminants on the items instead of improvements. The produced contaminants are immune to being washed away by water or destroyed by diff --git a/Readme.rst b/Readme.rst index 66f4017c7..ed2fe3ee8 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1103,7 +1103,7 @@ fix-armory Enables a fix for storage of squad equipment in barracks. -Specifically, it prevents your haulers from moving that equipment +Specifically, it prevents your haulers from moving squad equipment to stockpiles, and instead queues jobs to store it on weapon racks, armor stands, and in containers. @@ -1113,9 +1113,10 @@ armor stands, and in containers. manually assigned to a squad. See documentation for ``gui/assign-rack`` below. - Also, the default capacity of armor stands is way too low, so check out + Also, the default capacity of armor stands is way too low, so you + may want to also apply the ``armorstand-capacity`` patch. Check out http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 - for a patch addressing that too. + for more information about the bugs. Note that the buildings in the armory are used as follows: @@ -2165,7 +2166,9 @@ work again. The existing issues are: the game does this. This issue is what this script addresses. * Even if assigned by the script, **the game will unassign the racks again without a binary patch**. - Check the comments for this bug to get it: + This patch is called ``weaponrack-unassign``, and can be applied via + the binpatch program, or the matching script. See this for more info + about the bug: http://www.bay12games.com/dwarves/mantisbt/view.php?id=1445 * Haulers still take equpment stored in the armory away to the stockpiles, diff --git a/library/lua/binpatch.lua b/library/lua/binpatch.lua new file mode 100644 index 000000000..e957148f7 --- /dev/null +++ b/library/lua/binpatch.lua @@ -0,0 +1,121 @@ +-- Simple binary patch with IDA dif file support. + +local function load_patch(name) + local filename = name + if not string.match(filename, '[./\\]') then + filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif' + end + + local file, err = io.open(filename, 'r') + if not file then + if string.match(err, ': No such file or directory') then + return nil, 'patch not found' + end + end + + local old_bytes = {} + local new_bytes = {} + + for line in file:lines() do + if string.match(line, '^%x+:') then + local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$') + if not offset then + file:close() + return nil, 'could not parse: '..line + end + + offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16) + if oldv > 255 or newv > 255 then + file:close() + return nil, 'invalid byte values: '..line + end + + old_bytes[offset] = oldv + new_bytes[offset] = newv + end + end + + return { name = name, old_bytes = old_bytes, new_bytes = new_bytes } +end + +local function rebase_table(input) + local output = {} + local base = dfhack.internal.getImageBase() + for k,v in pairs(input) do + local offset = dfhack.internal.adjustOffset(k) + if not offset then + return nil, string.format('invalid offset: %x', k) + end + output[base + offset] = v + end + return output +end + +local function rebase_patch(patch) + local nold, err = rebase_table(patch.old_bytes) + if not nold then return nil, err end + local nnew, err = rebase_table(patch.new_bytes) + if not nnew then return nil, err end + return { name = patch.name, old_bytes = nold, new_bytes = nnew } +end + +BinaryPatch = defclass(BinaryPatch) + +BinaryPatch.ATTRS { + name = DEFAULT_NIL, + old_bytes = DEFAULT_NIL, + new_bytes = DEFAULT_NIL, +} + +function load_dif_file(name) + local patch, err = load_patch(name) + if not patch then return nil, err end + + local rpatch, err = rebase_patch(patch) + if not rpatch then return nil, err end + + return BinaryPatch(rpatch) +end + +function BinaryPatch:status() + local old_ok, err, addr = dfhack.internal.patchBytes({}, self.old_bytes) + if old_ok then + return 'removed' + elseif dfhack.internal.patchBytes({}, self.new_bytes) then + return 'applied' + else + return 'conflict', addr + end +end + +function BinaryPatch:isApplied() + return dfhack.internal.patchBytes({}, self.new_bytes) +end + +function BinaryPatch:apply() + local ok, err, addr = dfhack.internal.patchBytes(self.new_bytes, self.old_bytes) + if ok then + return true, 'applied the patch' + elseif dfhack.internal.patchBytes({}, self.new_bytes) then + return true, 'patch is already applied' + else + return false, string.format('conflict at address %x', addr) + end +end + +function BinaryPatch:isRemoved() + return dfhack.internal.patchBytes({}, self.old_bytes) +end + +function BinaryPatch:remove() + local ok, err, addr = dfhack.internal.patchBytes(self.old_bytes, self.new_bytes) + if ok then + return true, 'removed the patch' + elseif dfhack.internal.patchBytes({}, self.old_bytes) then + return true, 'patch is already removed' + else + return false, string.format('conflict at address %x', addr) + end +end + +return _ENV diff --git a/scripts/binpatch.lua b/scripts/binpatch.lua index f0f14e929..b9a4cf0b1 100644 --- a/scripts/binpatch.lua +++ b/scripts/binpatch.lua @@ -1,109 +1,36 @@ -- Apply or remove binary patches at runtime. -local utils = require('utils') - -function load_patch(name) - local filename = name - local auto = false - if not string.match(filename, '[./\\]') then - auto = true - filename = dfhack.getHackPath()..'/patches/'..dfhack.getDFVersion()..'/'..name..'.dif' - end - - local file, err = io.open(filename, 'r') - if not file then - if auto and string.match(err, ': No such file or directory') then - return nil, 'no patch '..name..' for '..dfhack.getDFVersion() - else - return nil, err - end - end - - local old_bytes = {} - local new_bytes = {} - - for line in file:lines() do - if string.match(line, '^%x+:') then - local offset, oldv, newv = string.match(line, '^(%x+):%s*(%x+)%s+(%x+)%s*$') - if not offset then - file:close() - return nil, 'Could not parse: '..line - end - - offset, oldv, newv = tonumber(offset,16), tonumber(oldv,16), tonumber(newv,16) - if oldv > 255 or newv > 255 then - file:close() - return nil, 'Invalid byte values: '..line - end - - old_bytes[offset] = oldv - new_bytes[offset] = newv - end - end - - return { name = name, old_bytes = old_bytes, new_bytes = new_bytes } -end - -function rebase_table(input) - local output = {} - local base = dfhack.internal.getImageBase() - for k,v in pairs(input) do - local offset = dfhack.internal.adjustOffset(k) - if not offset then - return nil, string.format('invalid offset: %x', k) - end - output[base + offset] = v - end - return output -end - -function rebase_patch(patch) - local nold, err = rebase_table(patch.old_bytes) - if not nold then return nil, err end - local nnew, err = rebase_table(patch.new_bytes) - if not nnew then return nil, err end - return { name = patch.name, old_bytes = nold, new_bytes = nnew } -end +local bp = require('binpatch') function run_command(cmd,name) - local patch, err = load_patch(name) - if not patch then - dfhack.printerr('Could not load: '..err) - return - end + local pfix = name..': ' - local rpatch, err = rebase_patch(patch) - if not rpatch then - dfhack.printerr(name..': '..err) + local patch, err = bp.load_dif_file(name) + if not patch then + dfhack.printerr(pfix..err) return end if cmd == 'check' then - local old_ok, err, addr = dfhack.internal.patchBytes({}, rpatch.old_bytes) - if old_ok then - print(name..': patch is not applied.') - elseif dfhack.internal.patchBytes({}, rpatch.new_bytes) then - print(name..': patch is applied.') + local status, addr = patch:status() + if status == 'conflict' then + dfhack.printerr(string.format('%sconflict at address %x', pfix, addr)) else - dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + print(pfix..'patch is '..status) end elseif cmd == 'apply' then - local ok, err, addr = dfhack.internal.patchBytes(rpatch.new_bytes, rpatch.old_bytes) + local ok, msg = patch:apply() if ok then - print(name..': applied the patch.') - elseif dfhack.internal.patchBytes({}, rpatch.new_bytes) then - print(name..': patch is already applied.') + print(pfix..msg) else - dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + dfhack.printerr(pfix..msg) end elseif cmd == 'remove' then - local ok, err, addr = dfhack.internal.patchBytes(rpatch.old_bytes, rpatch.new_bytes) + local ok, msg = patch:remove() if ok then - print(name..': removed the patch.') - elseif dfhack.internal.patchBytes({}, rpatch.old_bytes) then - print(name..': patch is already removed.') + print(pfix..msg) else - dfhack.printerr(string.format('%s: conflict at address %x', name, addr)) + dfhack.printerr(pfix..msg) end else qerror('Invalid command: '..cmd) diff --git a/scripts/gui/assign-rack.lua b/scripts/gui/assign-rack.lua index d358dfff1..92535d793 100644 --- a/scripts/gui/assign-rack.lua +++ b/scripts/gui/assign-rack.lua @@ -1,19 +1,13 @@ --- Assign weapon racks to squads. Requires patch from bug 1445. +-- Assign weapon racks to squads. Requires the weaponrack-unassign patch. ---[[ - - Required patches: - - v0.34.11 linux: http://pastebin.com/mt5EUgFZ - v0.34.11 windows: http://pastebin.com/09nRCybe - -]] +-- See bug 1445 for more info about the patches. local utils = require 'utils' local gui = require 'gui' local guidm = require 'gui.dwarfmode' local widgets = require 'gui.widgets' local dlg = require 'gui.dialogs' +local bp = require 'binpatch' AssignRack = defclass(AssignRack, guidm.MenuOverlay) @@ -190,12 +184,18 @@ end AssignRack{ building = dfhack.gui.getSelectedBuilding() }:show() -if not already_warned then - already_warned = true +if not already_patched then + local patch = bp.load_dif_file('weaponrack-unassign') + if patch and patch:isApplied() then + already_patched = true + end +end + +if not already_patched then dlg.showMessage( 'BUG ALERT', - { 'This script requires a binary patch from', NEWLINE, - 'bug 1445 on the tracker. Otherwise the game', NEWLINE, + { 'This script requires applying the binary patch', NEWLINE, + 'named weaponrack-unassign. Otherwise the game', NEWLINE, 'will lose your settings due to a bug.' }, COLOR_YELLOW ) From bd75cad508859ecb4f0d35000803de293d62cb3b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 12 Nov 2012 12:48:17 +0400 Subject: [PATCH 69/79] Support ! and ~ prefixes in the lua script, and edit readme. --- Readme.html | 23 +++++++++++++++-------- Readme.rst | 23 +++++++++++++++++++---- scripts/lua.lua | 31 +++++++++++++++++++++++++------ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/Readme.html b/Readme.html index b7ca94964..c596371e7 100644 --- a/Readme.html +++ b/Readme.html @@ -2734,15 +2734,22 @@ the creature.

                                                lua

                                                -
                                                -
                                                There are three ways to invoke this command:
                                                -
                                                  -
                                                1. without any parameters - starts an interactive lua interpreter
                                                2. -
                                                3. -f "filename" or --file "filename" - loads and runs the file indicated by filename
                                                4. -
                                                5. -s ["filename"] or --save ["filename"] - loads and runs the file indicated by filename from save directory. If filename is not supplied it loads "dfhack.lua"
                                                6. +

                                                  There are the following ways to invoke this command:

                                                  +
                                                    +
                                                  1. lua (without any parameters)

                                                    +

                                                    This starts an interactive lua interpreter.

                                                    +
                                                  2. +
                                                  3. lua -f "filename" or lua --file "filename"

                                                    +

                                                    This loads and runs the file indicated by filename.

                                                    +
                                                  4. +
                                                  5. lua -s ["filename"] or lua --save ["filename"]

                                                    +

                                                    This loads and runs the file indicated by filename from the save +directory. If the filename is not supplied, it loads "dfhack.lua".

                                                    +
                                                  6. +
                                                  7. :lua lua statement...

                                                    +

                                                    Parses and executes the lua statement like the interactive interpreter would.

                                                    +
                                                  -
                                                -

                                                embark

                                                diff --git a/Readme.rst b/Readme.rst index ed2fe3ee8..009d82d77 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1922,10 +1922,25 @@ the creature. lua === -There are three ways to invoke this command: - 1. without any parameters - starts an interactive lua interpreter - 2. -f "filename" or --file "filename" - loads and runs the file indicated by filename - 3. -s ["filename"] or --save ["filename"] - loads and runs the file indicated by filename from save directory. If filename is not supplied it loads "dfhack.lua" + +There are the following ways to invoke this command: + +1. ``lua`` (without any parameters) + + This starts an interactive lua interpreter. + +2. ``lua -f "filename"`` or ``lua --file "filename"`` + + This loads and runs the file indicated by filename. + +3. ``lua -s ["filename"]`` or ``lua --save ["filename"]`` + + This loads and runs the file indicated by filename from the save + directory. If the filename is not supplied, it loads "dfhack.lua". + +4. ``:lua`` *lua statement...* + + Parses and executes the lua statement like the interactive interpreter would. embark ====== diff --git a/scripts/lua.lua b/scripts/lua.lua index 556962347..9bf6ce793 100644 --- a/scripts/lua.lua +++ b/scripts/lua.lua @@ -1,11 +1,15 @@ +-- Execute lua commands interactively or from files. + local args={...} -if args[1]=="--file" or args[1]=="-f" then +local cmd = args[1] + +if cmd=="--file" or cmd=="-f" then local f,err=loadfile (args[2]) if f==nil then qerror(err) end dfhack.pcall(f,table.unpack(args,3)) -elseif args[1]=="--save" or args[1]=="-s" then +elseif cmd=="--save" or cmd=="-s" then if df.global.world.cur_savegame.save_dir=="" then qerror("Savefile not loaded") end @@ -16,12 +20,27 @@ elseif args[1]=="--save" or args[1]=="-s" then qerror(err) end dfhack.pcall(f,table.unpack(args,3)) -elseif args[1]~=nil then - local f,err=load(args[1],'=(lua command)', 't') +elseif cmd~=nil then + -- Support some of the prefixes allowed by dfhack.interpreter + local prefix + if string.match(cmd, "^[~!]") then + prefix = string.sub(cmd, 1, 1) + cmd = 'return '..string.sub(cmd, 2) + end + + local f,err=load(cmd,'=(lua command)', 't') if f==nil then qerror(err) end - dfhack.pcall(f,table.unpack(args,2)) + + local rv = table.pack(dfhack.safecall(f,table.unpack(args,2))) + + if rv[1] and prefix then + print(table.unpack(rv,2,rv.n)) + if prefix == '~' then + printall(rv[2]) + end + end else dfhack.interpreter("lua","lua.history") -end \ No newline at end of file +end From 766aca4911d72b4b6724c20e37e50fd5280b2e46 Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 12 Nov 2012 08:27:58 -0600 Subject: [PATCH 70/79] Rename general_ref vectors for consistency --- library/modules/Buildings.cpp | 4 ++-- library/modules/Items.cpp | 42 +++++++++++++++++------------------ library/modules/Job.cpp | 22 +++++++++--------- library/modules/Units.cpp | 10 ++++----- library/xml | 2 +- plugins/advtools.cpp | 8 +++---- plugins/autodump.cpp | 4 ++-- plugins/autolabor.cpp | 8 +++---- plugins/devel/stockcheck.cpp | 8 +++---- plugins/devel/stripcaged.cpp | 4 ++-- plugins/fix-armory.cpp | 4 ++-- plugins/ruby/building.rb | 4 ++-- plugins/showmood.cpp | 4 ++-- plugins/workflow.cpp | 8 +++---- plugins/zone.cpp | 38 +++++++++++++++---------------- 15 files changed, 85 insertions(+), 85 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 6421114a1..ca6cb2c17 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) @@ -895,7 +895,7 @@ static bool linkForConstruct(df::job* &job, df::building *bld) job = new df::job(); job->job_type = df::job_type::ConstructBuilding; job->pos = df::coord(bld->centerx, bld->centery, bld->z); - job->references.push_back(ref); + job->general_refs.push_back(ref); bld->jobs.push_back(job); diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 877f8abe0..4045e6baa 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) @@ -509,7 +509,7 @@ df::general_ref *Items::getGeneralRef(df::item *item, df::general_ref_type type) { CHECK_NULL_POINTER(item); - return findRef(item->itemrefs, type); + return findRef(item->general_refs, type); } df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type) @@ -530,9 +530,9 @@ bool Items::setOwner(df::item *item, df::unit *unit) { CHECK_NULL_POINTER(item); - for (int i = item->itemrefs.size()-1; i >= 0; i--) + for (int i = item->general_refs.size()-1; i >= 0; i--) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; if (!strict_virtual_cast(ref)) continue; @@ -546,7 +546,7 @@ bool Items::setOwner(df::item *item, df::unit *unit) } delete ref; - vector_erase_at(item->itemrefs, i); + vector_erase_at(item->general_refs, i); } item->flags.bits.owned = false; @@ -561,7 +561,7 @@ bool Items::setOwner(df::item *item, df::unit *unit) ref->unit_id = unit->id; insert_into_vector(unit->owned_items, item->id); - item->itemrefs.push_back(ref); + item->general_refs.push_back(ref); } return true; @@ -580,9 +580,9 @@ void Items::getContainedItems(df::item *item, std::vector *items) items->clear(); - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; if (ref->getType() != general_ref_type::CONTAINS_ITEM) continue; @@ -617,9 +617,9 @@ df::coord Items::getPosition(df::item *item) if (item->flags.bits.in_inventory) { - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; switch (ref->getType()) { @@ -716,9 +716,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) if (item->world_data_id != -1) return false; - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; switch (ref->getType()) { @@ -748,9 +748,9 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) { bool found = false; - for (int i = item->itemrefs.size()-1; i >= 0; i--) + for (int i = item->general_refs.size()-1; i >= 0; i--) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; switch (ref->getType()) { @@ -767,7 +767,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) item2->flags.bits.weight_computed = false; - removeRef(item2->itemrefs, general_ref_type::CONTAINS_ITEM, item->id); + removeRef(item2->general_refs, general_ref_type::CONTAINS_ITEM, item->id); } break; @@ -799,7 +799,7 @@ static bool detachItem(MapExtras::MapCache &mc, df::item *item) } found = true; - vector_erase_at(item->itemrefs, i); + vector_erase_at(item->general_refs, i); delete ref; } @@ -878,10 +878,10 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: container->flags.bits.weight_computed = false; ref1->item_id = item->id; - container->itemrefs.push_back(ref1); + container->general_refs.push_back(ref1); ref2->item_id = container->id; - item->itemrefs.push_back(ref2); + item->general_refs.push_back(ref2); return true; } @@ -912,7 +912,7 @@ bool DFHack::Items::moveToBuilding(MapExtras::MapCache &mc, df::item *item, df:: item->flags.bits.in_building=true; ref->building_id=building->id; - item->itemrefs.push_back(ref); + item->general_refs.push_back(ref); auto con=new df::building_actual::T_contained_items; con->item=item; @@ -955,7 +955,7 @@ bool DFHack::Items::moveToInventory( unit->inventory.push_back(newInventoryItem); holderReference->unit_id = unit->id; - item->itemrefs.push_back(holderReference); + item->general_refs.push_back(holderReference); resetUnitInvFlags(unit, newInventoryItem); @@ -1016,7 +1016,7 @@ df::proj_itemst *Items::makeProjectile(MapExtras::MapCache &mc, df::item *item) proj->item = item; ref->projectile_id = proj->id; - item->itemrefs.push_back(ref); + item->general_refs.push_back(ref); linked_list_append(&world->proj_list, proj->link); diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 757000885..db0fd73fe 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) @@ -71,14 +71,14 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job) pnew->specific_refs.clear(); // Clone refs - for (int i = pnew->references.size()-1; i >= 0; i--) + for (int i = pnew->general_refs.size()-1; i >= 0; i--) { - df::general_ref *ref = pnew->references[i]; + df::general_ref *ref = pnew->general_refs[i]; if (virtual_cast(ref)) - vector_erase_at(pnew->references, i); + vector_erase_at(pnew->general_refs, i); else - pnew->references[i] = ref->clone(); + pnew->general_refs[i] = ref->clone(); } // Clone items @@ -96,8 +96,8 @@ void DFHack::Job::deleteJobStruct(df::job *job) // Only allow free-floating job structs assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); - for (int i = job->references.size()-1; i >= 0; i--) - delete job->references[i]; + for (int i = job->general_refs.size()-1; i >= 0; i--) + delete job->general_refs[i]; for (int i = job->job_items.size()-1; i >= 0; i--) delete job->job_items[i]; @@ -232,9 +232,9 @@ df::building *DFHack::Job::getHolder(df::job *job) { CHECK_NULL_POINTER(job); - for (size_t i = 0; i < job->references.size(); i++) + for (size_t i = 0; i < job->general_refs.size(); i++) { - VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]); + VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]); if (ref) return ref->getBuilding(); } @@ -246,9 +246,9 @@ df::unit *DFHack::Job::getWorker(df::job *job) { CHECK_NULL_POINTER(job); - for (size_t i = 0; i < job->references.size(); i++) + for (size_t i = 0; i < job->general_refs.size(); i++) { - VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->references[i]); + VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]); if (ref) return ref->getUnit(); } diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 7a0a7549b..aa0459d08 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) @@ -523,9 +523,9 @@ df::item *Units::getContainer(df::unit *unit) { CHECK_NULL_POINTER(unit); - for (size_t i = 0; i < unit->refs.size(); i++) + for (size_t i = 0; i < unit->general_refs.size(); i++) { - df::general_ref *ref = unit->refs[i]; + df::general_ref *ref = unit->general_refs[i]; if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM) return ref->getItem(); } @@ -607,9 +607,9 @@ df::nemesis_record *Units::getNemesis(df::unit *unit) if (!unit) return NULL; - for (unsigned i = 0; i < unit->refs.size(); i++) + for (unsigned i = 0; i < unit->general_refs.size(); i++) { - df::nemesis_record *rv = unit->refs[i]->getNemesis(); + df::nemesis_record *rv = unit->general_refs[i]->getNemesis(); if (rv && rv->unit == unit) return rv; } diff --git a/library/xml b/library/xml index 4b2124957..327a9662b 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 4b2124957e282683480eaf05922e63c353364ec1 +Subproject commit 327a9662be81627ebcbb3aea11ffbca3e536b7ee diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index 59b88087b..5ceea69c8 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -331,7 +331,7 @@ typedef std::pair inv_item; static void listContainerInventory(std::vector *list, df::item *container) { - auto &refs = container->itemrefs; + auto &refs = container->general_refs; for (size_t i = 0; i < refs.size(); i++) { auto ref = refs[i]; @@ -372,9 +372,9 @@ void listUnitInventory(std::vector *list, df::unit *unit) bool isShopItem(df::item *item) { - for (size_t k = 0; k < item->itemrefs.size(); k++) + for (size_t k = 0; k < item->general_refs.size(); k++) { - auto ref = item->itemrefs[k]; + auto ref = item->general_refs[k]; if (virtual_cast(ref)) return true; } @@ -404,7 +404,7 @@ int containsMetalItems(df::item *item, bool all, bool non_trader, bool rec = fal { int cnt = 0; - auto &refs = item->itemrefs; + auto &refs = item->general_refs; for (size_t i = 0; i < refs.size(); i++) { auto ref = refs[i]; diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index cfb73fa8b..5eb25964e 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -277,9 +277,9 @@ command_result df_autodump_destroy_item(color_ostream &out, vector & pa return CR_FAILURE; } - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; if (ref->getType() == general_ref_type::UNIT_HOLDER) { out.printerr("Choosing not to destroy items in unit inventory.\n"); diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 44984cfdb..4ee5c0a54 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1582,9 +1582,9 @@ static int stockcheck(color_ostream &out, vector & parameters) df::unit *holder = 0; df::building *building = 0; - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; switch (ref->getType()) { @@ -1611,9 +1611,9 @@ static int stockcheck(color_ostream &out, vector & parameters) while(nextcontainer) { df::item *thiscontainer = nextcontainer; nextcontainer = 0; - for (size_t i = 0; i < thiscontainer->itemrefs.size(); i++) + for (size_t i = 0; i < thiscontainer->general_refs.size(); i++) { - df::general_ref *ref = thiscontainer->itemrefs[i]; + df::general_ref *ref = thiscontainer->general_refs[i]; switch (ref->getType()) { diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp index 452a637fc..57be4b126 100644 --- a/plugins/devel/stockcheck.cpp +++ b/plugins/devel/stockcheck.cpp @@ -170,9 +170,9 @@ static command_result stockcheck(color_ostream &out, vector & parameter df::unit *holder = 0; df::building *building = 0; - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; switch (ref->getType()) { @@ -199,9 +199,9 @@ static command_result stockcheck(color_ostream &out, vector & parameter while(nextcontainer) { df::item *thiscontainer = nextcontainer; nextcontainer = 0; - for (size_t i = 0; i < thiscontainer->itemrefs.size(); i++) + for (size_t i = 0; i < thiscontainer->general_refs.size(); i++) { - df::general_ref *ref = thiscontainer->itemrefs[i]; + df::general_ref *ref = thiscontainer->general_refs[i]; switch (ref->getType()) { diff --git a/plugins/devel/stripcaged.cpp b/plugins/devel/stripcaged.cpp index e3d2a82fc..a7683a9bb 100644 --- a/plugins/devel/stripcaged.cpp +++ b/plugins/devel/stripcaged.cpp @@ -46,9 +46,9 @@ DFHACK_PLUGIN("stripcaged"); bool isContainedInItem(df::unit* unit) { bool contained = false; - for (size_t r=0; r < unit->refs.size(); r++) + for (size_t r=0; r < unit->general_refs.size(); r++) { - df::general_ref * ref = unit->refs[r]; + df::general_ref * ref = unit->general_refs[r]; auto rtype = ref->getType(); if(rtype == df::general_ref_type::CONTAINED_IN_ITEM) { diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index 99e5fd500..efa9350ff 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -528,7 +528,7 @@ static bool try_store_item(df::building *target, df::item *item) // job <-> building link href->building_id = target->id; target->jobs.push_back(job); - job->references.push_back(href); + job->general_refs.push_back(href); // Two of the jobs need this link to find the job in canStoreItem(). // They also don't actually need BUILDING_HOLDER, but it doesn't hurt. @@ -539,7 +539,7 @@ static bool try_store_item(df::building *target, df::item *item) if (rdest) { rdest->building_id = target->id; - job->references.push_back(rdest); + job->general_refs.push_back(rdest); } } diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index e863ec5d5..37b91d166 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -275,7 +275,7 @@ module DFHack job = Job.cpp_new job.job_type = :ConstructBuilding job.pos = [bld.centerx, bld.centery, bld.z] - job.references << ref + job.general_refs << ref bld.jobs << job job_link job job @@ -346,7 +346,7 @@ module DFHack refbuildingholder = GeneralRefBuildingHolderst.cpp_new job.job_type = :DestroyBuilding refbuildingholder.building_id = bld.id - job.references << refbuildingholder + job.general_refs << refbuildingholder bld.jobs << job job_link job job diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 0b3fa8a99..7a3662f82 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -49,9 +49,9 @@ command_result df_showmood (color_ostream &out, vector & parameters) found = true; df::unit *unit = NULL; df::building *building = NULL; - for (size_t i = 0; i < job->references.size(); i++) + for (size_t i = 0; i < job->general_refs.size(); i++) { - df::general_ref *ref = job->references[i]; + df::general_ref *ref = job->general_refs[i]; if (ref->getType() == general_ref_type::UNIT_WORKER) unit = ref->getUnit(); if (ref->getType() == general_ref_type::BUILDING_HOLDER) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 04e3c13b0..6ba45cfd3 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -974,9 +974,9 @@ static void map_job_constraints(color_ostream &out) static void dryBucket(df::item *item) { - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; if (ref->getType() == general_ref_type::CONTAINS_ITEM) { df::item *obj = ref->getItem(); @@ -996,9 +996,9 @@ static bool itemBusy(df::item *item) { using namespace df::enums::item_type; - for (size_t i = 0; i < item->itemrefs.size(); i++) + for (size_t i = 0; i < item->general_refs.size(); i++) { - df::general_ref *ref = item->itemrefs[i]; + df::general_ref *ref = item->general_refs[i]; if (ref->getType() == general_ref_type::CONTAINS_ITEM) { df::item *obj = ref->getItem(); diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 2c63b6af7..15d310dc1 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -753,10 +753,10 @@ void unitInfo(color_ostream & out, df::unit* unit, bool verbose = false) if(!verbose) return; - //out << "number of refs: " << creature->refs.size() << endl; - for(size_t r = 0; rrefs.size(); r++) + //out << "number of refs: " << creature->general_refs.size() << endl; + for(size_t r = 0; rgeneral_refs.size(); r++) { - df::general_ref* ref = unit->refs.at(r); + df::general_ref* ref = unit->general_refs.at(r); df::general_ref_type refType = ref->getType(); out << " ref#" << r <<" refType#" << refType << " "; //endl; switch(refType) @@ -975,10 +975,10 @@ df::general_ref_building_civzone_assignedst * createCivzoneRef() for(size_t i = 0; i < world->units.all.size(); i++) { df::unit * creature = world->units.all[i]; - for(size_t r = 0; rrefs.size(); r++) + for(size_t r = 0; rgeneral_refs.size(); r++) { df::general_ref* ref; - ref = creature->refs.at(r); + ref = creature->general_refs.at(r); if(ref->getType() == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED) { if (strict_virtual_cast(ref)) @@ -1007,9 +1007,9 @@ bool isInBuiltCage(df::unit* unit); bool isAssigned(df::unit* unit) { bool assigned = false; - for (size_t r=0; r < unit->refs.size(); r++) + for (size_t r=0; r < unit->general_refs.size(); r++) { - df::general_ref * ref = unit->refs[r]; + df::general_ref * ref = unit->general_refs[r]; auto rtype = ref->getType(); if( rtype == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED || rtype == df::general_ref_type::BUILDING_CAGED @@ -1029,9 +1029,9 @@ bool isAssigned(df::unit* unit) bool isChained(df::unit* unit) { bool contained = false; - for (size_t r=0; r < unit->refs.size(); r++) + for (size_t r=0; r < unit->general_refs.size(); r++) { - df::general_ref * ref = unit->refs[r]; + df::general_ref * ref = unit->general_refs[r]; auto rtype = ref->getType(); if(rtype == df::general_ref_type::BUILDING_CHAIN) { @@ -1046,9 +1046,9 @@ bool isChained(df::unit* unit) bool isContainedInItem(df::unit* unit) { bool contained = false; - for (size_t r=0; r < unit->refs.size(); r++) + for (size_t r=0; r < unit->general_refs.size(); r++) { - df::general_ref * ref = unit->refs[r]; + df::general_ref * ref = unit->general_refs[r]; auto rtype = ref->getType(); if(rtype == df::general_ref_type::CONTAINED_IN_ITEM) { @@ -1280,14 +1280,14 @@ size_t countFreeEgglayers() bool unassignUnitFromBuilding(df::unit* unit) { bool success = false; - for (std::size_t idx = 0; idx < unit->refs.size(); idx++) + for (std::size_t idx = 0; idx < unit->general_refs.size(); idx++) { - df::general_ref * oldref = unit->refs[idx]; + df::general_ref * oldref = unit->general_refs[idx]; switch(oldref->getType()) { case df::general_ref_type::BUILDING_CIVZONE_ASSIGNED: { - unit->refs.erase(unit->refs.begin() + idx); + unit->general_refs.erase(unit->general_refs.begin() + idx); df::building_civzonest * oldciv = (df::building_civzonest *) oldref->getBuilding(); for(size_t oc=0; ocassigned_creature.size(); oc++) { @@ -1305,7 +1305,7 @@ bool unassignUnitFromBuilding(df::unit* unit) case df::general_ref_type::CONTAINED_IN_ITEM: { // game does not erase the ref until creature gets removed from cage - //unit->refs.erase(unit->refs.begin() + idx); + //unit->general_refs.erase(unit->general_refs.begin() + idx); // walk through buildings, check cages for inhabitants, compare ids for (size_t b=0; b < world->buildings.all.size(); b++) @@ -1335,7 +1335,7 @@ bool unassignUnitFromBuilding(df::unit* unit) case df::general_ref_type::BUILDING_CHAIN: { // try not erasing the ref and see what happens - //unit->refs.erase(unit->refs.begin() + idx); + //unit->general_refs.erase(unit->general_refs.begin() + idx); // probably need to delete chain reference here //success = true; break; @@ -1344,7 +1344,7 @@ bool unassignUnitFromBuilding(df::unit* unit) case df::general_ref_type::BUILDING_CAGED: { // not sure what to do here, doesn't seem to get used by the game - //unit->refs.erase(unit->refs.begin() + idx); + //unit->general_refs.erase(unit->general_refs.begin() + idx); //success = true; break; } @@ -1396,7 +1396,7 @@ command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building } ref->building_id = building->id; - unit->refs.push_back(ref); + unit->general_refs.push_back(ref); df::building_civzonest * civz = (df::building_civzonest *) building; civz->assigned_creature.push_back(unit->id); @@ -1437,7 +1437,7 @@ command_result assignUnitToCage(color_ostream& out, df::unit* unit, df::building } //ref->building_id = building->id; - //unit->refs.push_back(ref); + //unit->general_refs.push_back(ref); df::building_cagest* civz = (df::building_cagest*) building; civz->assigned_creature.push_back(unit->id); From 7a3de785ec534fd14055befb36dc8d29e630e625 Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 12 Nov 2012 08:29:44 -0600 Subject: [PATCH 71/79] Missed a few spots --- scripts/growcrops.rb | 4 ++-- scripts/gui/companion-order.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/growcrops.rb b/scripts/growcrops.rb index e3abe54ac..c591dff87 100644 --- a/scripts/growcrops.rb +++ b/scripts/growcrops.rb @@ -17,7 +17,7 @@ end inventory = Hash.new(0) df.world.items.other[:SEEDS].each { |seed| next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } + next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] inventory[seed.mat_index] += 1 } @@ -40,7 +40,7 @@ else df.world.items.other[:SEEDS].each { |seed| next if seed.mat_index != wantmat next if not seed.flags.in_building - next if not seed.itemrefs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } + next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] seed.grow_counter = @raws_plant_growdur[seed.mat_index] count += 1 diff --git a/scripts/gui/companion-order.lua b/scripts/gui/companion-order.lua index b9e3b8300..68bc7ab39 100644 --- a/scripts/gui/companion-order.lua +++ b/scripts/gui/companion-order.lua @@ -359,7 +359,7 @@ end}, v.riding_item_id=item.id local ref=df.general_ref_unit_riderst:new() ref.unit_id=v.id - item.itemrefs:insert("#",ref) + item.general_refs:insert("#",ref) end return true end}, From 55fcb7e3cade121bfb5455e7d4416b5e849c123f Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 12 Nov 2012 08:33:05 -0600 Subject: [PATCH 72/79] One more missed --- plugins/lua/dfusion/tools.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua index f1fdadf2b..3e24c169d 100644 --- a/plugins/lua/dfusion/tools.lua +++ b/plugins/lua/dfusion/tools.lua @@ -145,7 +145,7 @@ function project(unit,trg) --TODO add to menu? citem.next=newlink local proj_ref=df.general_ref_projectile:new() proj_ref.projectile_id=p.id - unit.refs:insert(#unit.refs,proj_ref) + unit.general_refs:insert(#unit.general_refs,proj_ref) unit.flags1.projectile=true end function empregnate(unit) From a99d47ee7a2ccbd8ebecac5fafb8803510d672fd Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 12 Nov 2012 08:38:29 -0600 Subject: [PATCH 73/79] Remove UTF-8 BOMs added by notepad --- library/modules/Buildings.cpp | 2 +- library/modules/Items.cpp | 2 +- library/modules/Job.cpp | 2 +- library/modules/Units.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index ca6cb2c17..de121bfa8 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 4045e6baa..91448ab3f 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index db0fd73fe..f913a71b6 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index aa0459d08..d22022914 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1,4 +1,4 @@ -/* +/* https://github.com/peterix/dfhack Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) From b4dcc7e7adf7c6e1deab4a0a77e0697610c8f6f3 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 12 Nov 2012 19:17:32 +0400 Subject: [PATCH 74/79] Add more native api functions for finding general and specific refs. --- Lua API.rst | 24 ++++++++++++++++++++++++ library/LuaApi.cpp | 6 ++++++ library/Types.cpp | 18 ++++++++++++++++++ library/include/Types.h | 4 ++++ library/include/modules/Buildings.h | 3 +++ library/include/modules/Job.h | 5 +++++ library/include/modules/Units.h | 3 +++ library/modules/Buildings.cpp | 14 ++++++++++++++ library/modules/Job.cpp | 15 +++++++++++++++ library/modules/Units.cpp | 23 +++++++++++++++-------- 10 files changed, 107 insertions(+), 8 deletions(-) diff --git a/Lua API.rst b/Lua API.rst index f89750e88..126bc7f9d 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -833,6 +833,14 @@ Job module Prints info about the job item. +* ``dfhack.job.getGeneralRef(job, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.job.getSpecificRef(job, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.job.getHolder(job)`` Returns the building holding the job. @@ -879,6 +887,14 @@ Units module Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. +* ``dfhack.units.getGeneralRef(unit, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.units.getSpecificRef(unit, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.units.getContainer(unit)`` Returns the container (cage) item or *nil*. @@ -1198,6 +1214,14 @@ Burrows module Buildings module ---------------- +* ``dfhack.buildings.getGeneralRef(building, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.buildings.getSpecificRef(building, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.buildings.setOwner(item,unit)`` Replaces the owner of the building. If unit is *nil*, removes ownership. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 3862530dd..b186316a8 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1121,6 +1121,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = { WRAPM(Job,cloneJobStruct), WRAPM(Job,printItemDetails), WRAPM(Job,printJobDetails), + WRAPM(Job,getGeneralRef), + WRAPM(Job,getSpecificRef), WRAPM(Job,getHolder), WRAPM(Job,getWorker), WRAPM(Job,checkBuildingsNow), @@ -1157,6 +1159,8 @@ static const luaL_Reg dfhack_job_funcs[] = { /***** Units module *****/ static const LuaWrapper::FunctionReg dfhack_units_module[] = { + WRAPM(Units, getGeneralRef), + WRAPM(Units, getSpecificRef), WRAPM(Units, getContainer), WRAPM(Units, setNickname), WRAPM(Units, getVisibleName), @@ -1427,6 +1431,8 @@ static bool buildings_containsTile(df::building *bld, int x, int y, bool room) { } static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { + WRAPM(Buildings, getGeneralRef), + WRAPM(Buildings, getSpecificRef), WRAPM(Buildings, setOwner), WRAPM(Buildings, allocInstance), WRAPM(Buildings, checkFreeTiles), diff --git a/library/Types.cpp b/library/Types.cpp index 1ba87f839..80d629934 100644 --- a/library/Types.cpp +++ b/library/Types.cpp @@ -100,6 +100,24 @@ bool DFHack::removeRef(std::vector &vec, df::general_ref_type return false; } +df::item *DFHack::findItemRef(std::vector &vec, df::general_ref_type type) +{ + auto ref = findRef(vec, type); + return ref ? ref->getItem() : NULL; +} + +df::building *DFHack::findBuildingRef(std::vector &vec, df::general_ref_type type) +{ + auto ref = findRef(vec, type); + return ref ? ref->getBuilding() : NULL; +} + +df::unit *DFHack::findUnitRef(std::vector &vec, df::general_ref_type type) +{ + auto ref = findRef(vec, type); + return ref ? ref->getUnit() : NULL; +} + df::specific_ref *DFHack::findRef(std::vector &vec, df::specific_ref_type type) { for (int i = vec.size()-1; i >= 0; i--) diff --git a/library/include/Types.h b/library/include/Types.h index 3b9bf00b6..98dbb7e74 100644 --- a/library/include/Types.h +++ b/library/include/Types.h @@ -80,6 +80,10 @@ namespace DFHack DFHACK_EXPORT df::general_ref *findRef(std::vector &vec, df::general_ref_type type); DFHACK_EXPORT bool removeRef(std::vector &vec, df::general_ref_type type, int id); + DFHACK_EXPORT df::item *findItemRef(std::vector &vec, df::general_ref_type type); + DFHACK_EXPORT df::building *findBuildingRef(std::vector &vec, df::general_ref_type type); + DFHACK_EXPORT df::unit *findUnitRef(std::vector &vec, df::general_ref_type type); + DFHACK_EXPORT df::specific_ref *findRef(std::vector &vec, df::specific_ref_type type); DFHACK_EXPORT bool removeRef(std::vector &vec, df::specific_ref_type type, void *ptr); }// namespace DFHack \ No newline at end of file diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 266aadcb8..53852efbb 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -92,6 +92,9 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); +DFHACK_EXPORT df::general_ref *getGeneralRef(df::building *building, df::general_ref_type type); +DFHACK_EXPORT df::specific_ref *getSpecificRef(df::building *building, df::specific_ref_type type); + /** * Sets the owner unit for the building. */ diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index 853813073..e33e8717c 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -28,6 +28,8 @@ distribution. #include "Export.h" #include "Module.h" +#include "Types.h" + #include #include "DataDefs.h" @@ -55,6 +57,9 @@ namespace DFHack DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); + DFHACK_EXPORT df::general_ref *getGeneralRef(df::job *job, df::general_ref_type type); + DFHACK_EXPORT df::specific_ref *getSpecificRef(df::job *job, df::specific_ref_type type); + DFHACK_EXPORT df::building *getHolder(df::job *job); DFHACK_EXPORT df::unit *getWorker(df::job *job); diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index ab1a5f63a..93e11afb1 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -205,6 +205,9 @@ DFHACK_EXPORT void CopyNameTo(df::unit *creature, df::language_name * target); /// Returns the true position of the unit (non-trivial in case of caged). DFHACK_EXPORT df::coord getPosition(df::unit *unit); +DFHACK_EXPORT df::general_ref *getGeneralRef(df::unit *unit, df::general_ref_type type); +DFHACK_EXPORT df::specific_ref *getSpecificRef(df::unit *unit, df::specific_ref_type type); + DFHACK_EXPORT df::item *getContainer(df::unit *unit); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index de121bfa8..1667a27ac 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -178,6 +178,20 @@ bool Buildings::ReadCustomWorkshopTypes(map & btypes) return true; } +df::general_ref *Buildings::getGeneralRef(df::building *building, df::general_ref_type type) +{ + CHECK_NULL_POINTER(building); + + return findRef(building->general_refs, type); +} + +df::specific_ref *Buildings::getSpecificRef(df::building *building, df::specific_ref_type type) +{ + CHECK_NULL_POINTER(building); + + return findRef(building->specific_refs, type); +} + bool Buildings::setOwner(df::building *bld, df::unit *unit) { CHECK_NULL_POINTER(bld); diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index f913a71b6..5a8a8c7a4 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -35,6 +35,7 @@ using namespace std; #include "Error.h" #include "PluginManager.h" #include "MiscUtils.h" +#include "Types.h" #include "modules/Job.h" #include "modules/Materials.h" @@ -228,6 +229,20 @@ void DFHack::Job::printJobDetails(color_ostream &out, df::job *job) printItemDetails(out, job->job_items[i], i); } +df::general_ref *Job::getGeneralRef(df::job *job, df::general_ref_type type) +{ + CHECK_NULL_POINTER(job); + + return findRef(job->general_refs, type); +} + +df::specific_ref *Job::getSpecificRef(df::job *job, df::specific_ref_type type) +{ + CHECK_NULL_POINTER(job); + + return findRef(job->specific_refs, type); +} + df::building *DFHack::Job::getHolder(df::job *job) { CHECK_NULL_POINTER(job); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index d22022914..ddbb8cb79 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -519,18 +519,25 @@ df::coord Units::getPosition(df::unit *unit) return unit->pos; } -df::item *Units::getContainer(df::unit *unit) +df::general_ref *Units::getGeneralRef(df::unit *unit, df::general_ref_type type) { CHECK_NULL_POINTER(unit); - for (size_t i = 0; i < unit->general_refs.size(); i++) - { - df::general_ref *ref = unit->general_refs[i]; - if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM) - return ref->getItem(); - } + return findRef(unit->general_refs, type); +} - return NULL; +df::specific_ref *Units::getSpecificRef(df::unit *unit, df::specific_ref_type type) +{ + CHECK_NULL_POINTER(unit); + + return findRef(unit->specific_refs, type); +} + +df::item *Units::getContainer(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + + return findItemRef(unit->general_refs, general_ref_type::CONTAINED_IN_ITEM); } static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) From bbe94c006f56c1c5ea181071473ef280d5bb7a93 Mon Sep 17 00:00:00 2001 From: Quietust Date: Mon, 12 Nov 2012 11:54:21 -0600 Subject: [PATCH 75/79] Update for temperaturest --- library/modules/Vegetation.cpp | 10 +++++----- plugins/steam-engine.cpp | 8 ++++---- plugins/tweak.cpp | 20 ++++++++++---------- scripts/fix/stable-temp.lua | 10 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library/modules/Vegetation.cpp b/library/modules/Vegetation.cpp index 1a6779553..f7c4c9b0c 100644 --- a/library/modules/Vegetation.cpp +++ b/library/modules/Vegetation.cpp @@ -71,15 +71,15 @@ bool Vegetation::copyPlant(const int32_t index, t_plant &out) out.material = out.origin->material; out.pos = out.origin->pos; out.grow_counter = out.origin->grow_counter; - out.temperature_1 = out.origin->temperature_1; - out.temperature_2 = out.origin->temperature_2; + out.temperature_1 = out.origin->temperature.whole; + out.temperature_2 = out.origin->temperature.fraction; out.is_burning = out.origin->is_burning; out.hitpoints = out.origin->hitpoints; out.update_order = out.origin->update_order; //out.unk1 = out.origin->anon_1; //out.unk2 = out.origin->anon_2; - //out.temperature_3 = out.origin->temperature_3; - //out.temperature_4 = out.origin->temperature_4; - //out.temperature_5 = out.origin->temperature_5; + //out.temperature_3 = out.origin->temperature_unk; + //out.temperature_4 = out.origin->min_safe_temp; + //out.temperature_5 = out.origin->max_safe_temp; return true; } diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index 60f38ef83..a8ba0e214 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -263,7 +263,7 @@ struct liquid_hook : df::item_liquid_miscst { DEFINE_VMETHOD_INTERPOSE(bool, checkTemperatureDamage, ()) { if (mat_state.whole & BOILING_FLAG) - temperature = std::max(int(temperature), getBoilingPoint()-1); + temperature.whole = std::max(int(temperature.whole), getBoilingPoint()-1); return INTERPOSE_NEXT(checkTemperatureDamage)(); } @@ -371,8 +371,8 @@ struct workshop_hook : df::building_workshopst { // Update flags liquid->flags.bits.in_building = true; liquid->mat_state.whole |= liquid_hook::BOILING_FLAG; - liquid->temperature = liquid->getBoilingPoint()-1; - liquid->temperature_fraction = 0; + liquid->temperature.whole = liquid->getBoilingPoint()-1; + liquid->temperature.fraction = 0; // This affects where the steam appears to come from if (engine->hearth_tile.isValid()) @@ -387,7 +387,7 @@ struct workshop_hook : df::building_workshopst { { liquid->wear = 4; liquid->flags.bits.in_building = false; - liquid->temperature = liquid->getBoilingPoint() + 10; + liquid->temperature.whole = liquid->getBoilingPoint() + 10; return liquid->checkMeltBoil(); } diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 93e5ab3bc..b4a435421 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -291,18 +291,18 @@ struct stable_temp_hook : df::item_actual { DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t rate_mult)) { - if (temperature != temp) + if (temperature.whole != temp) { // Bug 6012 is caused by fixed-point precision mismatch jitter // when an item is being pushed by two sources at N and N+1. // This check suppresses it altogether. - if (temp == temperature+1 || - (temp == temperature-1 && temperature_fraction == 0)) - temp = temperature; + if (temp == temperature.whole+1 || + (temp == temperature.whole-1 && temperature.fraction == 0)) + temp = temperature.whole; // When SPEC_HEAT is NONE, the original function seems to not // change the temperature, yet return true, which is silly. else if (getSpecHeat() == 60001) - temp = temperature; + temp = temperature.whole; } return INTERPOSE_NEXT(adjustTemperature)(temp, rate_mult); @@ -317,10 +317,10 @@ struct stable_temp_hook : df::item_actual { { auto obj = (*contaminants)[i]; - if (abs(obj->temperature - temperature) == 1) + if (abs(obj->temperature.whole - temperature.whole) == 1) { - obj->temperature = temperature; - obj->temperature_fraction = temperature_fraction; + obj->temperature.whole = temperature.whole; + obj->temperature.fraction = temperature.fraction; } } } @@ -355,11 +355,11 @@ struct fast_heat_hook : df::item_actual { (uint16_t temp, bool local, bool contained, bool adjust, int32_t rate_mult) ) { // Some items take ages to cross the last degree, so speed them up - if (map_temp_mult > 0 && temp != temperature && max_heat_ticks > 0) + if (map_temp_mult > 0 && temp != temperature.whole && max_heat_ticks > 0) { int spec = getSpecHeat(); if (spec != 60001) - rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature)); + rate_mult = std::max(map_temp_mult, spec/max_heat_ticks/abs(temp - temperature.whole)); } return INTERPOSE_NEXT(updateTemperature)(temp, local, contained, adjust, rate_mult); diff --git a/scripts/fix/stable-temp.lua b/scripts/fix/stable-temp.lua index 27a88ef7b..63739f8ea 100644 --- a/scripts/fix/stable-temp.lua +++ b/scripts/fix/stable-temp.lua @@ -8,20 +8,20 @@ local count = 0 local types = {} local function update_temp(item,btemp) - if item.temperature ~= btemp then + if item.temperature.whole ~= btemp then count = count + 1 local tid = item:getType() types[tid] = (types[tid] or 0) + 1 end if apply then - item.temperature = btemp - item.temperature_fraction = 0 + item.temperature.whole = btemp + item.temperature.fraction = 0 if item.contaminants then for _,c in ipairs(item.contaminants) do - c.temperature = btemp - c.temperature_fraction = 0 + c.temperature.whole = btemp + c.temperature.fraction = 0 end end end From dd89baf6f88b5e111dbe21b24a2f951f0799a5e2 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 12 Nov 2012 19:18:03 +0100 Subject: [PATCH 76/79] add raw mmap/mprotect access --- library/Process-darwin.cpp | 27 ++++++++++++++++++++++++- library/Process-linux.cpp | 27 ++++++++++++++++++++++++- library/Process-windows.cpp | 39 +++++++++++++++++++++++++++++++++++++ library/include/MemAccess.h | 21 ++++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/library/Process-darwin.cpp b/library/Process-darwin.cpp index d081c8c5c..72311e83a 100644 --- a/library/Process-darwin.cpp +++ b/library/Process-darwin.cpp @@ -304,4 +304,29 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange) result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect); return result==0; -} \ No newline at end of file +} + +// returns -1 on error +void* Process::memAlloc(const int length) +{ + return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); +} + +int Process::memDealloc(const void *ptr, const int length) +{ + return munmap(ptr, length); +} + +int Process::memProtect(const void *ptr, const int length, const int prot) +{ + int prot_native = 0; + + if (prot & Process::MemProt::READ) + prot_native |= PROT_READ; + if (prot & Process::MemProt::WRITE) + prot_native |= PROT_WRITE; + if (prot & Process::MemProt::EXEC) + prot_native |= PROT_EXEC; + + return mprotect(ptr, length, prot_native); +} diff --git a/library/Process-linux.cpp b/library/Process-linux.cpp index 046b7696d..c3995a2aa 100644 --- a/library/Process-linux.cpp +++ b/library/Process-linux.cpp @@ -235,4 +235,29 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange) result=mprotect((void *)range.start, (size_t)range.end-(size_t)range.start,protect); return result==0; -} \ No newline at end of file +} + +// returns -1 on error +void* Process::memAlloc(const int length) +{ + return mmap(0, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +} + +int Process::memDealloc(void *ptr, const int length) +{ + return munmap(ptr, length); +} + +int Process::memProtect(void *ptr, const int length, const int prot) +{ + int prot_native = 0; + + if (prot & Process::MemProt::READ) + prot_native |= PROT_READ; + if (prot & Process::MemProt::WRITE) + prot_native |= PROT_WRITE; + if (prot & Process::MemProt::EXEC) + prot_native |= PROT_EXEC; + + return mprotect(ptr, length, prot_native); +} diff --git a/library/Process-windows.cpp b/library/Process-windows.cpp index 6f79236f9..cfa0b688d 100644 --- a/library/Process-windows.cpp +++ b/library/Process-windows.cpp @@ -473,3 +473,42 @@ bool Process::setPermisions(const t_memrange & range,const t_memrange &trgrange) return result; } + +void* Process::memAlloc(const int length) +{ + void *ret; + // returns 0 on error + ret = VirtualAlloc(0, length, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + if (!ret) + ret = (void*)-1; + return ret; +} + +int Process::memDealloc(const void *ptr, const int length) +{ + // can only free the whole region at once + // vfree returns 0 on error + return !VirtualFree(ptr, 0, MEM_RELEASE) +} + +int Process::memProtect(const void *ptr, const int length, const int prot) +{ + int prot_native = 0; + int old_prot = 0; + + // only support a few constant combinations + if (prot == 0) + prot_native = PAGE_NOACCESS; + else if (prot == Process::MemProt::READ) + prot_native = PAGE_READONLY; + else if (prot == Process::MemProt::READ | Process::MemProt::WRITE) + prot_native = PAGE_READWRITE; + else if (prot == Process::MemProt::READ | Process::MemProt::WRITE | Process::MemProt::EXECUTE) + prot_native = PAGE_EXECUTE_READWRITE; + else if (prot == Process::MemProt::READ | Process::MemProt::EXECUTE) + prot_native = PAGE_EXECUTE_READ; + else + return -1; + + return !VirtualProtect(ptr, length, prot_native, &old_prot); +} diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index 22f15eecf..31647a25e 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -291,6 +291,27 @@ namespace DFHack /// write a possibly read-only memory area bool patchMemory(void *target, const void* src, size_t count); + + /// allocate new memory pages for code or stuff + /// returns -1 on error (0 is a valid address) + void* memAlloc(const int length); + + /// free memory pages from memAlloc + /// should have length = alloced length for portability + /// returns 0 on success + int memDealloc(void *ptr, const int length); + + /// change memory page permissions + /// prot is a bitwise OR of the MemProt enum + /// returns 0 on success + int memProtect(void *ptr, const int length, const int prot); + + enum MemProt { + READ = 1, + WRITE = 2, + EXEC = 4 + }; + private: VersionInfo * my_descriptor; PlatformSpecific *d; From 72912edf58ea562592f00c8bab308c13529427a2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 16 Nov 2012 18:45:51 +0400 Subject: [PATCH 77/79] Ensure AddPersistentData won't create duplicate ids. If anything messes around with the histfig vector between calls. --- library/modules/World.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/modules/World.cpp b/library/modules/World.cpp index f3283c3a3..9ae4266b2 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -197,11 +197,15 @@ PersistentDataItem World::AddPersistentData(const std::string &key) std::vector &hfvec = df::historical_figure::get_vector(); df::historical_figure *hfig = new df::historical_figure(); - hfig->id = next_persistent_id--; + hfig->id = next_persistent_id; hfig->name.has_name = true; hfig->name.first_name = key; memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); + if (!hfvec.empty()) + hfig->id = std::min(hfig->id, hfvec[0]->id-1); + next_persistent_id = hfig->id-1; + hfvec.insert(hfvec.begin(), hfig); persistent_index.insert(T_persistent_item(key, -hfig->id)); From 2401be1b3b4ed423542e232deeafa8406ffc79b8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 16 Nov 2012 22:48:49 +0400 Subject: [PATCH 78/79] Add an api function to retrieve unit skill experience. --- Lua API.html | 21 +++++++++++++++++++++ Lua API.rst | 4 ++++ library/LuaApi.cpp | 1 + library/include/modules/Units.h | 2 ++ library/modules/Units.cpp | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+) diff --git a/Lua API.html b/Lua API.html index d2e0da1ef..04af5d672 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1109,6 +1109,12 @@ above operations accordingly. If enabled, pauses and zooms to position.

                                              • dfhack.job.printItemDetails(jobitem,idx)

                                                Prints info about the job item.

                                              • +
                                              • dfhack.job.getGeneralRef(job, type)

                                                +

                                                Searches for a general_ref with the given type.

                                                +
                                              • +
                                              • dfhack.job.getSpecificRef(job, type)

                                                +

                                                Searches for a specific_ref with the given type.

                                                +
                                              • dfhack.job.getHolder(job)

                                                Returns the building holding the job.

                                              • @@ -1147,6 +1153,12 @@ the flags in the job item.

                                              • dfhack.units.getPosition(unit)

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

                                              • +
                                              • dfhack.units.getGeneralRef(unit, type)

                                                +

                                                Searches for a general_ref with the given type.

                                                +
                                              • +
                                              • dfhack.units.getSpecificRef(unit, type)

                                                +

                                                Searches for a specific_ref with the given type.

                                                +
                                              • dfhack.units.getContainer(unit)

                                                Returns the container (cage) item or nil.

                                              • @@ -1209,6 +1221,9 @@ is true, subtracts the rust penalty.

                                              • dfhack.units.getEffectiveSkill(unit, skill)

                                                Computes the effective rating for the given skill, taking into account exhaustion, pain etc.

                                              • +
                                              • dfhack.units.getExperience(unit, skill[, total])

                                                +

                                                Returns the experience value for the given skill. If total is true, adds experience implied by the current rating.

                                                +
                                              • dfhack.units.computeMovementSpeed(unit)

                                                Computes number of frames * 100 it takes the unit to move in its current state of mind and body.

                                              • @@ -1403,6 +1418,12 @@ burrows, or the presence of invaders.

                                                Buildings module

                                                  +
                                                • dfhack.buildings.getGeneralRef(building, type)

                                                  +

                                                  Searches for a general_ref with the given type.

                                                  +
                                                • +
                                                • dfhack.buildings.getSpecificRef(building, type)

                                                  +

                                                  Searches for a specific_ref with the given type.

                                                  +
                                                • dfhack.buildings.setOwner(item,unit)

                                                  Replaces the owner of the building. If unit is nil, removes ownership. Returns false in case of error.

                                                  diff --git a/Lua API.rst b/Lua API.rst index 126bc7f9d..4087ff0aa 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -970,6 +970,10 @@ Units module Computes the effective rating for the given skill, taking into account exhaustion, pain etc. +* ``dfhack.units.getExperience(unit, skill[, total])`` + + Returns the experience value for the given skill. If ``total`` is true, adds experience implied by the current rating. + * ``dfhack.units.computeMovementSpeed(unit)`` Computes number of frames * 100 it takes the unit to move in its current state of mind and body. diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b186316a8..a4ebbea5b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1180,6 +1180,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getAge), WRAPM(Units, getNominalSkill), WRAPM(Units, getEffectiveSkill), + WRAPM(Units, getExperience), WRAPM(Units, computeMovementSpeed), WRAPM(Units, getProfessionName), WRAPM(Units, getCasteProfessionName), diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 93e11afb1..c2eb7ca18 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -238,6 +238,8 @@ DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); DFHACK_EXPORT int getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust = false); DFHACK_EXPORT int getEffectiveSkill(df::unit *unit, df::job_skill skill_id); +DFHACK_EXPORT int getExperience(df::unit *unit, df::job_skill skill_id, bool total = false); + DFHACK_EXPORT int computeMovementSpeed(df::unit *unit); struct NoblePosition { diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index ddbb8cb79..b19399c6c 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -909,6 +909,24 @@ int Units::getNominalSkill(df::unit *unit, df::job_skill skill_id, bool use_rust return 0; } +int Units::getExperience(df::unit *unit, df::job_skill skill_id, bool total) +{ + CHECK_NULL_POINTER(unit); + + if (!unit->status.current_soul) + return 0; + + auto skill = binsearch_in_vector(unit->status.current_soul->skills, &df::unit_skill::id, skill_id); + if (!skill) + return 0; + + int xp = skill->experience; + // exact formula used by the game: + if (total && skill->rating > 0) + xp += 500*skill->rating + 100*skill->rating*(skill->rating - 1)/2; + return xp; +} + int Units::getEffectiveSkill(df::unit *unit, df::job_skill skill_id) { /* From d506dd713717aedb4ef98a087834845c13ac92c7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 16 Nov 2012 22:51:07 +0400 Subject: [PATCH 79/79] Add a tweak to speed up melee squad training. --- NEWS | 2 + dfhack.init-example | 3 + library/xml | 2 +- plugins/tweak.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 48aaf26a2..39b4ccf02 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ DFHack future - fastdwarf: new mode using debug flags, and some internal consistency fixes. - added a small stand-alone utility for applying and removing binary patches. - removebadthoughts: add --dry-run option + New tweaks: + - tweak military-training: speed up melee squad training up to 10x (depends on unit count). New scripts: - binpatch: the same as the stand-alone binpatch.exe, but works at runtime. - region-pops: displays animal populations of the region and allows tweaking them. diff --git a/dfhack.init-example b/dfhack.init-example index 885849c33..8fafa4cf4 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -133,6 +133,9 @@ tweak military-stable-assign # in same list, color units already assigned to squads in brown & green tweak military-color-assigned +# remove inverse dependency of squad training speed on unit list size and use more sparring +tweak military-training + ####################################################### # Apply binary patches at runtime # # # diff --git a/library/xml b/library/xml index 327a9662b..9b4dc47a5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 327a9662be81627ebcbb3aea11ffbca3e536b7ee +Subproject commit 9b4dc47a54c8b15db2f30fbf926deb8c1bf992f6 diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index b4a435421..c2309a3ee 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -51,6 +51,12 @@ #include "df/squad_position.h" #include "df/job.h" #include "df/general_ref_building_holderst.h" +#include "df/unit_health_info.h" +#include "df/activity_entry.h" +#include "df/activity_event_combat_trainingst.h" +#include "df/activity_event_individual_skill_drillst.h" +#include "df/activity_event_skill_demonstrationst.h" +#include "df/activity_event_sparringst.h" #include @@ -128,6 +134,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector counters2.exhaustion <= 2000 && // actually 4000, but leave a gap + (unit->status2.able_grasp_impair > 0 || unit->status2.able_grasp == 0) && + (!unit->health || (unit->health->flags.whole&0x7FF) == 0) && + (!unit->job.current_job || unit->job.current_job != job_type::Rest); +} + +struct military_training_ct_hook : df::activity_event_combat_trainingst { + typedef df::activity_event_combat_trainingst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) + { + auto act = df::activity_entry::find(activity_id); + int cur_neid = act ? act->next_event_id : 0; + int cur_oc = organize_counter; + + INTERPOSE_NEXT(process)(unit); + + // Shorten the time it takes to organize stuff, so that in + // reality it remains the same instead of growing proportionally + // to the unit count. + if (organize_counter > cur_oc && organize_counter > 0) + organize_counter = adjust_unit_divisor(organize_counter); + + if (act && act->next_event_id > cur_neid) + { + // New events were added. Check them. + for (size_t i = 0; i < act->events.size(); i++) + { + auto event = act->events[i]; + if (event->flags.bits.dismissed || event->event_id < cur_neid) + continue; + + if (auto sp = strict_virtual_cast(event)) + { + // Sparring has a problem in that all of its participants decrement + // the countdown variable. Fix this by multiplying it by the member count. + sp->countdown = sp->countdown * sp->participants.hist_figure_ids.size(); + } + else if (auto sd = strict_virtual_cast(event)) + { + // Adjust initial counter values + sd->train_countdown = adjust_unit_divisor(sd->train_countdown); + sd->wait_countdown = adjust_unit_divisor(sd->wait_countdown); + + // Check if the game selected the most skilled unit as the teacher + auto &units = sd->participants.participant_ids; + int maxv = -1, cur_xp = -1, minv = 0; + int best = -1; + size_t spar = 0; + + for (size_t j = 0; j < units.size(); j++) + { + auto unit = df::unit::find(units[j]); + if (!unit) continue; + int xp = Units::getExperience(unit, sd->skill, true); + if (units[j] == sd->unit_id) + cur_xp = xp; + if (j == 0 || xp < minv) + minv = xp; + if (xp > maxv) { + maxv = xp; + best = j; + } + if (can_spar(unit)) + spar++; + } + + color_ostream_proxy out(Core::getInstance().getConsole()); + + // If the xp gap is low, sometimes replace with sparring + if ((maxv - minv) < 64*15 && spar == units.size() && + random_int(20) >= 5 + (maxv-minv)/64) + { + out.print("Replacing demonstration with sparring.\n"); + + if (auto spar = df::allocate()) + { + spar->event_id = sd->event_id; + spar->activity_id = sd->activity_id; + spar->parent_event_id = sd->parent_event_id; + spar->flags = sd->flags; + spar->participants = sd->participants; + spar->building_id = sd->building_id; + spar->countdown = 300*units.size(); + + delete sd; + act->events[i] = spar; + + continue; + } + } + + // If the teacher has less xp than somebody else, switch + if (best >= 0 && maxv > cur_xp) + { + out.print("Replacing teacher %d (%d xp) with %d (%d xp)\n", + sd->unit_id, cur_xp, units[best], maxv); + + sd->hist_figure_id = sd->participants.hist_figure_ids[best]; + sd->unit_id = units[best]; + } + } + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_training_ct_hook, process); + +struct military_training_sd_hook : df::activity_event_skill_demonstrationst { + typedef df::activity_event_skill_demonstrationst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) + { + int cur_oc = organize_counter; + int cur_tc = train_countdown; + + INTERPOSE_NEXT(process)(unit); + + // Shorten the counters if they changed + if (organize_counter > cur_oc && organize_counter > 0) + organize_counter = adjust_unit_divisor(organize_counter); + if (train_countdown > cur_tc) + train_countdown = adjust_unit_divisor(train_countdown); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_training_sd_hook, process); + +static bool is_done(df::activity_event *event, df::unit *unit) +{ + return event->flags.bits.dismissed || + binsearch_index(event->participants.participant_ids, unit->id) < 0; +} + +struct military_training_sp_hook : df::activity_event_sparringst { + typedef df::activity_event_sparringst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) + { + INTERPOSE_NEXT(process)(unit); + + // Since there are no counters to fix, repeat the call + int cnt = (DF_GLOBAL_FIELD(ui, unit_action_divisor, 10)+5) / 10; + for (int i = 1; i < cnt && !is_done(this, unit); i++) + INTERPOSE_NEXT(process)(unit); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_training_sp_hook, process); + +struct military_training_id_hook : df::activity_event_individual_skill_drillst { + typedef df::activity_event_individual_skill_drillst interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, process, (df::unit *unit)) + { + INTERPOSE_NEXT(process)(unit); + + // Since there are no counters to fix, repeat the call + int cnt = (DF_GLOBAL_FIELD(ui, unit_action_divisor, 10)+5) / 10; + for (int i = 1; i < cnt && !is_done(this, unit); i++) + INTERPOSE_NEXT(process)(unit); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(military_training_id_hook, process); + static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -835,6 +1017,13 @@ static command_result tweak(color_ostream &out, vector ¶meters) { enable_hook(out, INTERPOSE_HOOK(military_assign_hook, render), parameters); } + else if (cmd == "military-training") + { + enable_hook(out, INTERPOSE_HOOK(military_training_ct_hook, process), parameters); + enable_hook(out, INTERPOSE_HOOK(military_training_sd_hook, process), parameters); + enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters); + enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters); + } else return CR_WRONG_USAGE;