From 3d277823ebe5f0d140f11e28b427df83d03b4fde Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 28 Apr 2012 22:20:49 +0300 Subject: [PATCH 01/31] Misc stuff needed for unreleased unfunctional stuff --- plugins/Dfusion/luafiles/common.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index 7e41dc4e5..347b27c0a 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -99,6 +99,7 @@ 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 @@ -224,6 +225,11 @@ function engine.LoadModData(file) 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]) From 519ea08e8fa03863186cf75e0674a394d3dd4fc6 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 28 Apr 2012 22:21:35 +0300 Subject: [PATCH 02/31] Typo fix --- plugins/Dfusion/luafiles/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index e68684bf7..6fa86d7ac 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -81,7 +81,7 @@ 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) advneturer hacking"}) +table.insert(plugins,{"adv_tools","some tools for (mainly) adventurer hacking"}) loadall(plugins) dofile_silent("dfusion/initcustom.lua") From 9046fed648376d53071773fb3b7fbcf569a3ae59 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 24 Jun 2012 12:51:19 +0400 Subject: [PATCH 03/31] Add documentation for some utility functions implemented in lua. --- LUA_API.rst | 189 ++++++++++++++++++++++++++++++++++++++++-- Lua API.html | 187 ++++++++++++++++++++++++++++++++++++++--- library/lua/utils.lua | 12 +-- 3 files changed, 364 insertions(+), 24 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 5fc653bb3..252be3374 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -17,7 +17,7 @@ are treated by DFHack command line prompt almost as native C++ commands, and invoked by plugins written in c++. This document describes native API available to Lua in detail. -For the most part it does not describe utility functions +It does not describe all of the utility functions implemented by Lua files located in hack/lua/... @@ -1323,9 +1323,9 @@ Features: order using ``dfhack.safecall``. -======= -Modules -======= +=========== +Lua Modules +=========== DFHack sets up the lua interpreter so that the built-in ``require`` function can be used to load shared lua code from hack/lua/. @@ -1333,7 +1333,7 @@ The ``dfhack`` namespace reference itself may be obtained via ``require('dfhack')``, although it is initially created as a global by C++ bootstrap code. -The following functions are provided: +The following module management functions are provided: * ``mkmodule(name)`` @@ -1357,6 +1357,182 @@ The following functions are provided: should be kept limited to the standard Lua library and API described in this document. +Global environment +================== + +A number of variables and functions are provided in the base global +environment by the mandatory init file dfhack.lua: + +* Color constants + + These are applicable both for ``dfhack.color()`` and color fields + in DF functions or structures: + + COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, + COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY, + COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED, + COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE + +* ``dfhack.onStateChange`` event codes + + Available only in the core context, as is the event itself: + + SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, + SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED + +* Functions already described above + + safecall, qerror, mkmodule, reload + +* ``printall(obj)`` + + If the argument is a lua table or DF object reference, prints all fields. + +* ``copyall(obj)`` + + Returns a shallow copy of the table or reference as a lua table. + +* ``pos2xyz(obj)`` + + The object must have fields x, y and z. Returns them as 3 values. + If obj is *nil*, or x is -30000 (the usual marker for undefined + coordinates), returns *nil*. + +* ``xyz2pos(x,y,z)`` + + Returns a table with x, y and z as fields. + +* ``safe_index(obj,index...)`` + + Walks a sequence of dereferences, which may be represented by numbers or strings. + Returns *nil* if any of obj or indices is *nil*, or a numeric index is out of array bounds. + +utils +===== + +* ``utils.compare(a,b)`` + + Comparator function; returns *-1* if ab, *0* otherwise. + +* ``utils.compare_name(a,b)`` + + Comparator for names; compares empty string last. + +* ``utils.is_container(obj)`` + + Checks if obj is a container ref. + +* ``utils.make_index_sequence(start,end)`` + + Returns a lua sequence of numbers in start..end. + +* ``utils.make_sort_order(data, ordering)`` + + Computes an ordering of objects in data, as a table of integer + indices into the data sequence. Uses ``data.n`` as input length + if present. + + The ordering argument is a sequence of ordering specs, represented + as lua tables with following possible fields: + + ord.key = *function(value)* + Computes comparison key from a data value. Not called on nil. + If omitted, the comparison key is the value itself. + ord.key_table = *function(data)* + Computes a key table from the data table in one go. + ord.compare = *function(a,b)* + Comparison function. Defaults to ``utils.compare`` above. + Called on non-nil keys; nil sorts last. + ord.nil_first = *true/false* + If true, nil keys are sorted first instead of last. + ord.reverse = *true/false* + If true, sort non-nil keys in descending order. + + This function is used by the sort plugin. + +* ``utils.assign(tgt, src)`` + + Does a recursive assignment of src into tgt. + Uses ``df.assign`` if tgt is a native object ref; otherwise + recurses into lua tables. + +* ``utils.clone(obj, deep)`` + + Performs a shallow, or semi-deep copy of the object as a lua table tree. + The deep mode recurses into lua tables and subobjects, except pointers + to other heap objects. + Null pointers are represented as df.NULL. Zero-based native containers + are converted to 1-based lua sequences. + +* ``utils.clone_with_default(obj, default, force)`` + + Copies the object, using the ``default`` lua table tree + as a guide to which values should be skipped as uninteresting. + The ``force`` argument makes it always return a non-*nil* value. + +* ``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.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. + If ``min`` and ``max`` are specified, they are used as the + search subrange bounds. + + If found, returns *item, true, idx*. Otherwise returns + *nil, false, insert_idx*, where *insert_idx* is the correct + insertion point. + +* ``utils.insert_sorted(vector,item,field,cmpfun)`` + + Does a binary search, and inserts item if not found. + Returns *did_insert, vector[idx], idx*. + +* ``utils.insert_or_update(vector,item,field,cmpfun)`` + + Like ``insert_sorted``, but also assigns the item into + the vector cell if insertion didn't happen. + + As an example, you can use this to set skill values:: + + utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id') + + (For an explanation of ``new=true``, see table assignment in the wrapper section) + +* ``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. + If the user enters ``'abort'``, throws an error. + +* ``utils.prompt_input(prompt, checkfun, quit_str)`` + + Presents a prompt to input data, until a valid string is entered. + Once ``checkfun(input)`` returns *true, ...*, passes the values + through. If the user enters the quit_str (defaults to ``'~~~'``), + throws an error. + +* ``utils.check_number(text)`` + + A ``prompt_input`` ``checkfun`` that verifies a number input. + +dumper +====== + +A third-party lua table dumper module from +http://lua-users.org/wiki/DataDumper. Defines one +function: + +* ``dumper.DataDumper(value, varname, fastmode, ident, indent_step)`` + + Returns ``value`` converted to a string. The ``indent_step`` + argument specifies the indentation step size in spaces. For + the other arguments see the original documentation link above. + ======= Plugins @@ -1430,6 +1606,9 @@ are automatically used by the DFHack core as commands. The matching command name consists of the name of the file sans the extension. +If the first line of the script is a one-line comment, it is +used by the built-in ``ls`` and ``help`` commands. + **NOTE:** Scripts placed in subdirectories still can be accessed, but do not clutter the ``ls`` command list; thus it is preferred for obscure developer-oriented scripts and scripts used by tools. diff --git a/Lua API.html b/Lua API.html index 04e899366..0496d7e50 100644 --- a/Lua API.html +++ b/Lua API.html @@ -360,13 +360,18 @@ ul.auto-toc { -
  • Modules
  • -
  • Plugins

    The current version of DFHack has extensive support for @@ -381,7 +386,7 @@ structures, and interaction with dfhack itself.

  • are treated by DFHack command line prompt almost as native C++ commands, and invoked by plugins written in c++.

    This document describes native API available to Lua in detail. -For the most part it does not describe utility functions +It does not describe all of the utility functions implemented by Lua files located in hack/lua/...

    DF data structure wrapper

    @@ -1480,14 +1485,14 @@ order using dfhack.safecall.

    -
    -

    Modules

    +
    +

    Lua Modules

    DFHack sets up the lua interpreter so that the built-in require function can be used to load shared lua code from hack/lua/. The dfhack namespace reference itself may be obtained via require('dfhack'), although it is initially created as a global by C++ bootstrap code.

    -

    The following functions are provided:

    +

    The following module management functions are provided:

    • mkmodule(name)

      Creates an environment table for the module. Intended to be used as:

      @@ -1509,16 +1514,172 @@ should be kept limited to the standard Lua library and API described in this document.

    +
    +

    Global environment

    +

    A number of variables and functions are provided in the base global +environment by the mandatory init file dfhack.lua:

    +
      +
    • Color constants

      +

      These are applicable both for dfhack.color() and color fields +in DF functions or structures:

      +

      COLOR_RESET, COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, +COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_GREY, COLOR_DARKGREY, +COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN, COLOR_LIGHTRED, +COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE

      +
    • +
    • dfhack.onStateChange event codes

      +

      Available only in the core context, as is the event itself:

      +

      SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, +SC_MAP_UNLOADED, SC_VIEWSCREEN_CHANGED, SC_CORE_INITIALIZED

      +
    • +
    • Functions already described above

      +

      safecall, qerror, mkmodule, reload

      +
    • +
    • printall(obj)

      +

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

      +
    • +
    • copyall(obj)

      +

      Returns a shallow copy of the table or reference as a lua table.

      +
    • +
    • pos2xyz(obj)

      +

      The object must have fields x, y and z. Returns them as 3 values. +If obj is nil, or x is -30000 (the usual marker for undefined +coordinates), returns nil.

      +
    • +
    • xyz2pos(x,y,z)

      +

      Returns a table with x, y and z as fields.

      +
    • +
    • safe_index(obj,index...)

      +

      Walks a sequence of dereferences, which may be represented by numbers or strings. +Returns nil if any of obj or indices is nil, or a numeric index is out of array bounds.

      +
    • +
    +
    +
    +

    utils

    +
      +
    • utils.compare(a,b)

      +

      Comparator function; returns -1 if a<b, 1 if a>b, 0 otherwise.

      +
    • +
    • utils.compare_name(a,b)

      +

      Comparator for names; compares empty string last.

      +
    • +
    • utils.is_container(obj)

      +

      Checks if obj is a container ref.

      +
    • +
    • utils.make_index_sequence(start,end)

      +

      Returns a lua sequence of numbers in start..end.

      +
    • +
    • utils.make_sort_order(data, ordering)

      +

      Computes an ordering of objects in data, as a table of integer +indices into the data sequence. Uses data.n as input length +if present.

      +

      The ordering argument is a sequence of ordering specs, represented +as lua tables with following possible fields:

      +
      +
      ord.key = function(value)
      +

      Computes comparison key from a data value. Not called on nil. +If omitted, the comparison key is the value itself.

      +
      +
      ord.key_table = function(data)
      +

      Computes a key table from the data table in one go.

      +
      +
      ord.compare = function(a,b)
      +

      Comparison function. Defaults to utils.compare above. +Called on non-nil keys; nil sorts last.

      +
      +
      ord.nil_first = true/false
      +

      If true, nil keys are sorted first instead of last.

      +
      +
      ord.reverse = true/false
      +

      If true, sort non-nil keys in descending order.

      +
      +
      +

      This function is used by the sort plugin.

      +
    • +
    • utils.assign(tgt, src)

      +

      Does a recursive assignment of src into tgt. +Uses df.assign if tgt is a native object ref; otherwise +recurses into lua tables.

      +
    • +
    • utils.clone(obj, deep)

      +

      Performs a shallow, or semi-deep copy of the object as a lua table tree. +The deep mode recurses into lua tables and subobjects, except pointers +to other heap objects. +Null pointers are represented as df.NULL. Zero-based native containers +are converted to 1-based lua sequences.

      +
    • +
    • utils.clone_with_default(obj, default, force)

      +

      Copies the object, using the default lua table tree +as a guide to which values should be skipped as uninteresting. +The force argument makes it always return a non-nil value.

      +
    • +
    • 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.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. +If min and max are specified, they are used as the +search subrange bounds.

      +

      If found, returns item, true, idx. Otherwise returns +nil, false, insert_idx, where insert_idx is the correct +insertion point.

      +
    • +
    • utils.insert_sorted(vector,item,field,cmpfun)

      +

      Does a binary search, and inserts item if not found. +Returns did_insert, vector[idx], idx.

      +
    • +
    • utils.insert_or_update(vector,item,field,cmpfun)

      +

      Like insert_sorted, but also assigns the item into +the vector cell if insertion didn't happen.

      +

      As an example, you can use this to set skill values:

      +
      +utils.insert_or_update(soul.skills, {new=true, id=..., rating=...}, 'id')
      +
      +

      (For an explanation of new=true, see table assignment in the wrapper section)

      +
    • +
    • 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. +If the user enters 'abort', throws an error.

      +
    • +
    • utils.prompt_input(prompt, checkfun, quit_str)

      +

      Presents a prompt to input data, until a valid string is entered. +Once checkfun(input) returns true, ..., passes the values +through. If the user enters the quit_str (defaults to '~~~'), +throws an error.

      +
    • +
    • utils.check_number(text)

      +

      A prompt_input checkfun that verifies a number input.

      +
    • +
    +
    +
    +

    dumper

    +

    A third-party lua table dumper module from +http://lua-users.org/wiki/DataDumper. Defines one +function:

    +
      +
    • dumper.DataDumper(value, varname, fastmode, ident, indent_step)

      +

      Returns value converted to a string. The indent_step +argument specifies the indentation step size in spaces. For +the other arguments see the original documentation link above.

      +
    • +
    +
    -

    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:

      @@ -1556,13 +1717,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/library/lua/utils.lua b/library/lua/utils.lua index f303091d6..38a1e6c42 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -57,10 +57,10 @@ function is_container(obj) end -- Make a sequence of numbers in 1..size -function make_index_sequence(size) +function make_index_sequence(istart,iend) local index = {} - for i=1,size do - index[i] = i + for i=istart,iend do + index[i-istart+1] = i end return index end @@ -114,7 +114,7 @@ function make_sort_order(data,ordering) end -- Make an order table - local index = make_index_sequence(size) + local index = make_index_sequence(1,size) -- Sort the ordering table table.sort(index, function(ia,ib) @@ -379,7 +379,7 @@ function prompt_yes_no(msg,default) elseif string.match(rv,'^[Nn]') then return false elseif rv == 'abort' then - qerror('User abort in utils.prompt_yes_no()') + qerror('User abort') elseif rv == '' and default ~= nil then return default end @@ -393,7 +393,7 @@ function prompt_input(prompt,check,quit_str) while true do local rv = dfhack.lineedit(prompt) if rv == quit_str then - return nil + qerror('User abort') end local rtbl = table.pack(check(rv)) if rtbl[1] then From 1c013486d5e39c9c946cda40e3bdd71136010a14 Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 25 Jun 2012 22:22:20 +0300 Subject: [PATCH 04/31] Removed non-working onfunction. --- .../Dfusion/luafiles/onfunction/compile.bat | 1 - .../Dfusion/luafiles/onfunction/functions.asm | 23 ------ .../Dfusion/luafiles/onfunction/functions.o | Bin 404 -> 0 bytes plugins/Dfusion/luafiles/onfunction/init.lua | 68 ------------------ .../Dfusion/luafiles/onfunction/locations.lua | 16 ----- .../Dfusion/luafiles/onfunction/plugin.lua | 15 ---- 6 files changed, 123 deletions(-) delete mode 100644 plugins/Dfusion/luafiles/onfunction/compile.bat delete mode 100644 plugins/Dfusion/luafiles/onfunction/functions.asm delete mode 100644 plugins/Dfusion/luafiles/onfunction/functions.o delete mode 100644 plugins/Dfusion/luafiles/onfunction/init.lua delete mode 100644 plugins/Dfusion/luafiles/onfunction/locations.lua delete mode 100644 plugins/Dfusion/luafiles/onfunction/plugin.lua diff --git a/plugins/Dfusion/luafiles/onfunction/compile.bat b/plugins/Dfusion/luafiles/onfunction/compile.bat deleted file mode 100644 index f06fb8c4c..000000000 --- a/plugins/Dfusion/luafiles/onfunction/compile.bat +++ /dev/null @@ -1 +0,0 @@ -as -anl --32 -o functions.o functions.asm \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/onfunction/functions.asm b/plugins/Dfusion/luafiles/onfunction/functions.asm deleted file mode 100644 index 13ef23191..000000000 --- a/plugins/Dfusion/luafiles/onfunction/functions.asm +++ /dev/null @@ -1,23 +0,0 @@ -.intel_syntax -push eax -push ebp -push esp -push esi -push edi -push edx -push ecx -push ebx -push eax -mov eax,[esp+36] -push eax -function: -call 0xdeadbee0 -function2: -mov [0xdeadbeef],eax -pop eax -function3: -jmp [0xdeadbeef] - - - - diff --git a/plugins/Dfusion/luafiles/onfunction/functions.o b/plugins/Dfusion/luafiles/onfunction/functions.o deleted file mode 100644 index 7b7d4a33f044cd95156cb204eede5e0cb155714a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 404 zcmeZaWM%+?Z9vQoX0b5pm84dbfY}g20Z8=#@frx1fe}b6Fc>80r6iUlLd9@V4h#kd z^pc8;ammBP8-VHpLPNsBg93vCx?NOM0$#k|xAxv*5E1cT6+}(o2P*y#1R@N2X_+~x zP_6&|GccqxrX^;>1fb$+rFqFEnfZBqKt9ODY+!K~ASsL@0+N@6iy-?Qq>GtR9x4Z< hpa9w5Oh7SakU}(o>}OC2V^bpm6o material esp->block type - onfunction.AddFunction(0x3d4301+offsets.base(),"Make_Item",{item_type="esp"}) - onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}}) - onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"}) - onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=] - --onfunction.AddFunction(0x4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx? 0.34.07 - onfunction.AddFunction(0x72aB6+offsets.base(),"Die",{creature="edi"}) --0.34.07 -else --linux - --[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch... - onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=] -end diff --git a/plugins/Dfusion/luafiles/onfunction/plugin.lua b/plugins/Dfusion/luafiles/onfunction/plugin.lua deleted file mode 100644 index 60360817c..000000000 --- a/plugins/Dfusion/luafiles/onfunction/plugin.lua +++ /dev/null @@ -1,15 +0,0 @@ -mypos=engine.getmod("functions") -function DeathMsg(values) - local name - local u=engine.cast(df.unit,values[onfunction.hints["Die"].creature]) - - print(u.name.first_name.." died") -end -if mypos then - print("Onfunction already installed") - --onfunction.patch(0x189dd6+offsets.base()) -else - onfunction.install() - dofile("dfusion/onfunction/locations.lua") - onfunction.SetCallback("Die",DeathMsg) -end From 85f9d35f6f4897eb44a6b893bdf4822983ec7a3d Mon Sep 17 00:00:00 2001 From: Warmist Date: Mon, 25 Jun 2012 22:26:08 +0300 Subject: [PATCH 05/31] Temporary fix for memory accessor. --- plugins/Dfusion/luafiles/common.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index bdcda5e89..752d07cf6 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -36,7 +36,7 @@ function GetTextRegion() --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,".text") or string.find(v.name,"libs/Dwarf_Fortress") + 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; From f0d4d799d838eacc1cc5d4a6eb267fcd9d62e748 Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 27 Jun 2012 16:38:39 -0500 Subject: [PATCH 06/31] Don't index world_data->region_map beyond its bounds --- plugins/probe.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/probe.cpp b/plugins/probe.cpp index b7b7d298f..2ae6846d5 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -221,10 +221,11 @@ command_result df_probe (color_ostream &out, vector & parameters) out.print("temperature2: %d U\n",mc.temperature2At(cursor)); int offset = block.region_offset[des.bits.biome]; - df::coord2d region_pos = block.region_pos + df::coord2d ((offset % 3) - 1, (offset / 3) -1); + int bx = clip_range(block.region_pos.x + (offset % 3) - 1, 0, world->world_data->world_width-1); + int by = clip_range(block.region_pos.y + (offset / 3) - 1, 0, world->world_data->world_height-1); df::world_data::T_region_map* biome = - &world->world_data->region_map[region_pos.x][region_pos.y]; + &world->world_data->region_map[bx][by]; int sav = biome->savagery; int evi = biome->evilness; From 07dc20055a599811b6ae517467d6a75392e47a60 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 30 Jun 2012 16:25:41 +0400 Subject: [PATCH 07/31] Tweak documentation for utils.make_sort_order and devel/prepare-save --- LUA_API.rst | 16 ++++++++++++++-- Lua API.html | 19 ++++++++++++++++--- scripts/devel/prepare-save.lua | 15 +++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 252be3374..2bb2c949e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1428,7 +1428,7 @@ utils * ``utils.make_sort_order(data, ordering)`` - Computes an ordering of objects in data, as a table of integer + Computes a sorted permutation of objects in data, as a table of integer indices into the data sequence. Uses ``data.n`` as input length if present. @@ -1436,7 +1436,7 @@ utils as lua tables with following possible fields: ord.key = *function(value)* - Computes comparison key from a data value. Not called on nil. + Computes comparison key from input data value. Not called on nil. If omitted, the comparison key is the value itself. ord.key_table = *function(data)* Computes a key table from the data table in one go. @@ -1448,6 +1448,18 @@ utils ord.reverse = *true/false* If true, sort non-nil keys in descending order. + For every comparison during sorting the specs are applied in + order until an unambiguous decision is reached. Sorting is stable. + + Example of sorting a sequence by field foo:: + + local spec = { key = function(v) return v.foo end } + local order = utils.make_sort_order(data, { spec }) + local output = {} + for i = 1,#order do output[i] = data[order[i]] end + + Separating the actual reordering of the sequence in this + way enables applying the same permutation to multiple arrays. This function is used by the sort plugin. * ``utils.assign(tgt, src)`` diff --git a/Lua API.html b/Lua API.html index 0496d7e50..2c9a6a8df 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1571,14 +1571,14 @@ Returns nil if any of obj or indices is nil, or a numeric inde

    Returns a lua sequence of numbers in start..end.

  • utils.make_sort_order(data, ordering)

    -

    Computes an ordering of objects in data, as a table of integer +

    Computes a sorted permutation of objects in data, as a table of integer indices into the data sequence. Uses data.n as input length if present.

    The ordering argument is a sequence of ordering specs, represented as lua tables with following possible fields:

    ord.key = function(value)
    -

    Computes comparison key from a data value. Not called on nil. +

    Computes comparison key from input data value. Not called on nil. If omitted, the comparison key is the value itself.

    ord.key_table = function(data)
    @@ -1595,7 +1595,18 @@ Called on non-nil keys; nil sorts last.

    If true, sort non-nil keys in descending order.

    -

    This function is used by the sort plugin.

    +

    For every comparison during sorting the specs are applied in +order until an unambiguous decision is reached. Sorting is stable.

    +

    Example of sorting a sequence by field foo:

    +
    +local spec = { key = function(v) return v.foo end }
    +local order = utils.make_sort_order(data, { spec })
    +local output = {}
    +for i = 1,#order do output[i] = data[order[i]] end
    +
    +

    Separating the actual reordering of the sequence in this +way enables applying the same permutation to multiple arrays. +This function is used by the sort plugin.

  • utils.assign(tgt, src)

    Does a recursive assignment of src into tgt. @@ -1728,6 +1739,8 @@ calls lua code to perform the actual ordering of list items.

    are automatically used by the DFHack core as commands. The matching command name consists of the name of the file sans the extension.

    +

    If the first line of the script is a one-line comment, it is +used by the built-in ls and help commands.

    NOTE: Scripts placed in subdirectories still can be accessed, but do not clutter the ls command list; thus it is preferred for obscure developer-oriented scripts and scripts used by tools. diff --git a/scripts/devel/prepare-save.lua b/scripts/devel/prepare-save.lua index 781e3b892..c282c8a43 100644 --- a/scripts/devel/prepare-save.lua +++ b/scripts/devel/prepare-save.lua @@ -1,7 +1,22 @@ -- Prepare the current save for use with devel/find-offsets. +local utils = require 'utils' + df.global.pause_state = true +print[[ +WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. + +This script prepares the current savegame to be used +with devel/find-offsets. It CHANGES THE GAME STATE +to predefined values, and initiates an immediate +quicksave, thus PERMANENTLY MODIFYING the save. +]] + +if not utils.prompt_yes_no('Proceed?') then + return +end + --[[print('Placing anchor...') do From b9425474a1006a2f713462e171894f476c58bbb7 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 2 Jul 2012 21:57:53 +0200 Subject: [PATCH 08/31] ruby: fix codegen for darwin --- plugins/ruby/codegen.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index c7fb210c9..1fcebfffe 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -8,7 +8,7 @@ use XML::LibXML; our @lines_rb; my $os; -if ($^O =~ /linux/i) { +if ($^O =~ /linux/i or $^O =~ /darwin/i) { $os = 'linux'; } else { $os = 'windows'; From 0bdff401ca75d85490db621be367c80068e8e448 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 14:12:32 +0200 Subject: [PATCH 09/31] ruby: fix codegen align int64_t on windows --- plugins/ruby/codegen.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 1fcebfffe..67b86f835 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -516,7 +516,9 @@ sub get_field_align { if ($meta eq 'number') { $al = $field->getAttribute('ld:bits')/8; - $al = 4 if $al > 4; + # linux aligns int64_t to 4, windows to 8 + # floats are 4 bytes so no pb + $al = 4 if ($al > 4 and ($os eq 'linux' or $al != 8)); } elsif ($meta eq 'global') { $al = get_global_align($field); } elsif ($meta eq 'compound') { From a7a25b80a0349d967a762b66ef12132f256da879 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 16:10:10 +0200 Subject: [PATCH 10/31] ruby: use the color_ostream argument from dfhack to output to dfhack-run --- library/Core.cpp | 6 +++--- library/PluginManager.cpp | 2 +- library/include/PluginManager.h | 4 ++-- plugins/ruby/ruby.cpp | 37 ++++++++++++++++++++++----------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 09344135c..59334906d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -281,7 +281,7 @@ static command_result runLuaScript(color_ostream &out, std::string name, vector< return ok ? CR_OK : CR_FAILURE; } -static command_result runRubyScript(PluginManager *plug_mgr, std::string name, vector &args) +static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr, std::string name, vector &args) { std::string rbcmd = "$script_args = ["; for (size_t i = 0; i < args.size(); i++) @@ -290,7 +290,7 @@ static command_result runRubyScript(PluginManager *plug_mgr, std::string name, v rbcmd += "load './hack/scripts/" + name + ".rb'"; - return plug_mgr->eval_ruby(rbcmd.c_str()); + return plug_mgr->eval_ruby(out, rbcmd.c_str()); } command_result Core::runCommand(color_ostream &out, const std::string &command) @@ -632,7 +632,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve if (fileExists(filename + ".lua")) res = runLuaScript(con, first, parts); else if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) - res = runRubyScript(plug_mgr, first, parts); + res = runRubyScript(con, plug_mgr, first, parts); else con.printerr("%s is not a recognized command.\n", first.c_str()); } diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index a314883e1..ff7524318 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -188,7 +188,7 @@ bool Plugin::load(color_ostream &con) plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); - plugin_eval_ruby = (command_result (*)(const char*)) LookupPlugin(plug, "plugin_eval_ruby"); + plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); this->name = *plug_name; plugin_lib = plug; diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 5da9fc92f..22171a15c 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -209,7 +209,7 @@ namespace DFHack command_result (*plugin_onupdate)(color_ostream &); command_result (*plugin_onstatechange)(color_ostream &, state_change_event); RPCService* (*plugin_rpcconnect)(color_ostream &); - command_result (*plugin_eval_ruby)(const char*); + command_result (*plugin_eval_ruby)(color_ostream &, const char*); }; class DFHACK_EXPORT PluginManager { @@ -238,7 +238,7 @@ namespace DFHack { return all_plugins.size(); } - command_result (*eval_ruby)(const char*); + command_result (*eval_ruby)(color_ostream &, const char*); // DATA private: tthread::mutex * cmdlist_mutex; diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 0f5264515..f1f007263 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -35,9 +35,12 @@ tthread::mutex *m_irun; tthread::mutex *m_mutex; static volatile RB_command r_type; static volatile command_result r_result; +static color_ostream *r_console; // color_ostream given as argument, if NULL resort to console_proxy static const char *r_command; static tthread::thread *r_thread; static int onupdate_active; +static color_ostream_proxy *console_proxy; + DFHACK_PLUGIN("ruby") @@ -115,7 +118,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) } // send a single ruby line to be evaluated by the ruby thread -DFhackCExport command_result plugin_eval_ruby(const char *command) +DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *command) { // if dlopen failed if (!r_thread) @@ -136,6 +139,7 @@ DFhackCExport command_result plugin_eval_ruby(const char *command) r_type = RB_EVAL; r_command = command; + r_console = &out; // wake ruby thread up m_irun->unlock(); @@ -144,6 +148,7 @@ DFhackCExport command_result plugin_eval_ruby(const char *command) tthread::this_thread::yield(); ret = r_result; + r_console = NULL; // block ruby thread m_irun->lock(); @@ -164,7 +169,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (!onupdate_active) return CR_OK; - return plugin_eval_ruby("DFHack.onupdate"); + return plugin_eval_ruby(out, "DFHack.onupdate"); } DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) @@ -187,7 +192,7 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch #undef SCASE } - return plugin_eval_ruby(cmd.c_str()); + return plugin_eval_ruby(out, cmd.c_str()); } static command_result df_rubyeval(color_ostream &out, std::vector & parameters) @@ -209,7 +214,7 @@ static command_result df_rubyeval(color_ostream &out, std::vector full += " "; } - return plugin_eval_ruby(full.c_str()); + return plugin_eval_ruby(out, full.c_str()); } @@ -265,7 +270,7 @@ static int df_loadruby(void) #if defined(WIN32) "./libruby.dll"; #elif defined(__APPLE__) - "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib"; + "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib"; #else "hack/libruby.so"; #endif @@ -310,6 +315,13 @@ static void df_unloadruby(void) } } +static void printerr(const char* fmt, const char *arg) +{ + if (r_console) + r_console->printerr(fmt, arg); + else + Core::printerr(fmt, arg); +} // ruby thread code static void dump_rb_error(void) @@ -320,19 +332,17 @@ static void dump_rb_error(void) s = rb_funcall(err, rb_intern("class"), 0); s = rb_funcall(s, rb_intern("name"), 0); - Core::printerr("E: %s: ", rb_string_value_ptr(&s)); + printerr("E: %s: ", rb_string_value_ptr(&s)); s = rb_funcall(err, rb_intern("message"), 0); - Core::printerr("%s\n", rb_string_value_ptr(&s)); + printerr("%s\n", rb_string_value_ptr(&s)); err = rb_funcall(err, rb_intern("backtrace"), 0); for (int i=0 ; i<8 ; ++i) if ((s = rb_ary_shift(err)) != Qnil) - Core::printerr(" %s\n", rb_string_value_ptr(&s)); + printerr(" %s\n", rb_string_value_ptr(&s)); } -static color_ostream_proxy *console_proxy; - // ruby thread main loop static void df_rubythread(void *p) { @@ -428,13 +438,16 @@ static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val) static VALUE rb_dfprint_str(VALUE self, VALUE s) { - console_proxy->print("%s", rb_string_value_ptr(&s)); + if (r_console) + r_console->print("%s", rb_string_value_ptr(&s)); + else + console_proxy->print("%s", rb_string_value_ptr(&s)); return Qnil; } static VALUE rb_dfprint_err(VALUE self, VALUE s) { - Core::printerr("%s", rb_string_value_ptr(&s)); + printerr("%s", rb_string_value_ptr(&s)); return Qnil; } From 1d6160de02c72513ab5e1b89152daae976744545 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 17:41:09 +0200 Subject: [PATCH 11/31] added fixstuckdoors.rb script --- scripts/fixstuckdoors.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 scripts/fixstuckdoors.rb diff --git a/scripts/fixstuckdoors.rb b/scripts/fixstuckdoors.rb new file mode 100644 index 000000000..619ceeeb6 --- /dev/null +++ b/scripts/fixstuckdoors.rb @@ -0,0 +1,20 @@ +# fix doors that are frozen in 'open' state + +# door is stuck in open state if the map occupancy flag incorrectly indicates +# that an unit is present (and creatures will prone to pass through) + +count = 0 +df.world.buildings.all.each { |bld| + # for all doors + next if bld._rtti_classname != :building_doorst + # check if it is open + next if bld.close_timer == 0 + # check if occupancy is set + occ = df.map_occupancy_at(bld.x1, bld.y1, bld.z) + next if not occ.unit + # check if an unit is present + next if df.world.units.active.find { |u| u.pos.x == bld.x1 and u.pos.y == bld.y1 and u.pos.z == bld.z } + count += 1 + occ.unit = false +} +puts "unstuck #{count} doors" From 501fd43dda221fb94aae2da4ce4cfba8e349ae5c Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 18:40:10 +0200 Subject: [PATCH 12/31] ruby: fix handling of --- plugins/ruby/ruby-autogen-defs.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 64da12ff9..3c004b41a 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -277,6 +277,7 @@ module DFHack def _get addr = _getp return if addr == 0 + return addr if not @_tg @_tg._at(addr)._get end From 2e42e31754720e2bce830cec9b3a69b2966c15d5 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 19:51:52 +0200 Subject: [PATCH 13/31] ruby: add curview, fix item_find/unit_find wrt gview mode, fix refers-to to use binsearch --- plugins/ruby/codegen.pl | 21 ++++++++++++++++++--- plugins/ruby/item.rb | 16 ++++++++++++---- plugins/ruby/ui.rb | 7 +++++++ plugins/ruby/unit.rb | 21 +++++++++++++-------- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 67b86f835..9ec1eb788 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -298,8 +298,21 @@ sub render_field_reftarget { return if (!$tg); my $tgvec = $tg->getAttribute('instance-vector'); return if (!$tgvec); + my $idx = $tg->getAttribute('key-field') || 'id'; - render_field_refto($parent, $name, $tgvec); + $tgvec =~ s/^\$global/df/; + return if $tgvec !~ /^[\w\.]+$/; + + my $tgname = "${name}_tg"; + $tgname =~ s/_id(.?.?)_tg/_tg$1/; + + for my $othername (map { $_->getAttribute('name') } $parent->findnodes('child::ld:field')) { + $tgname .= '_' if ($othername and $tgname eq $othername); + } + + my $fidx = ''; + $fidx = ', :' . $idx if ($idx ne 'id'); + push @lines_rb, "def $tgname ; ${tgvec}.binsearch($name$fidx) ; end"; } sub render_field_refto { @@ -329,9 +342,9 @@ sub render_container_reftarget { return if (!$tg); my $tgvec = $tg->getAttribute('instance-vector'); return if (!$tgvec); + my $idx = $tg->getAttribute('key-field') || 'id'; $tgvec =~ s/^\$global/df/; - $tgvec =~ s/\[\$\]$//; return if $tgvec !~ /^[\w\.]+$/; my $tgname = "${name}_tg"; @@ -341,7 +354,9 @@ sub render_container_reftarget { $tgname .= '_' if ($othername and $tgname eq $othername); } - push @lines_rb, "def $tgname ; $name.map { |i| ${tgvec}[i] } ; end"; + my $fidx = ''; + $fidx = ', :' . $idx if ($idx ne 'id'); + push @lines_rb, "def $tgname ; $name.map { |i| $tgvec.binsearch(i$fidx) } ; end"; } sub render_class_vmethods { diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index cd95e82a9..87917bad5 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -4,10 +4,18 @@ module DFHack # arg similar to unit.rb/unit_find; no arg = 'k' menu def item_find(what=:selected) if what == :selected - case ui.main.mode - when :LookAround - k = ui_look_list.items[ui_look_cursor] - k.item if k.type == :Item + if curview._rtti_classname == :viewscreen_itemst + ref = curview.entry_ref[curview.cursor_pos] + ref.item_tg if ref.kind_of?(GeneralRefItem) + else + case ui.main.mode + when :LookAround + k = ui_look_list.items[ui_look_cursor] + k.item if k.type == :Item + when :BuildingItems + bld = world.selected_building + bld.contained_items[ui_building_item_cursor].item if bld + end end elsif what.kind_of?(Integer) world.items.all.binsearch(what) diff --git a/plugins/ruby/ui.rb b/plugins/ruby/ui.rb index fbe7ced77..6d2b5c2cd 100644 --- a/plugins/ruby/ui.rb +++ b/plugins/ruby/ui.rb @@ -1,6 +1,13 @@ # df user-interface related methods module DFHack class << self + # returns the current active viewscreen + def curview + ret = gview.view + ret = ret.child while ret.child + ret + end + # center the DF screen on something # updates the cursor position if visible def center_viewscreen(x, y=nil, z=nil) diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index e7d4335f0..b2f23e25f 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -6,14 +6,19 @@ module DFHack # with an argument that respond to x/y/z (eg cursor), find first unit at this position def unit_find(what=:selected) if what == :selected - case ui.main.mode - when :ViewUnits - # nobody selected => idx == 0 - v = world.units.active[ui_selected_unit] - v if v and v.pos.z == cursor.z - when :LookAround - k = ui_look_list.items[ui_look_cursor] - k.unit if k.type == :Unit + if curview._rtti_classname == :viewscreen_itemst + ref = curview.entry_ref[curview.cursor_pos] + ref.unit_tg if ref.kind_of?(GeneralRefUnit) + else + case ui.main.mode + when :ViewUnits + # nobody selected => idx == 0 + v = world.units.active[ui_selected_unit] + v if v and v.pos.z == cursor.z + when :LookAround + k = ui_look_list.items[ui_look_cursor] + k.unit if k.type == :Unit + end end elsif what.kind_of?(Integer) world.units.all.binsearch(what) From 877b879e57b6c080a38df1d3a1af27530acf4eb2 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 3 Jul 2012 23:57:15 +0200 Subject: [PATCH 14/31] ruby: item_find handle v/i unit inventory --- plugins/ruby/item.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 87917bad5..5df1aed87 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -15,6 +15,10 @@ module DFHack when :BuildingItems bld = world.selected_building bld.contained_items[ui_building_item_cursor].item if bld + when :ViewUnits + u = world.units.active[ui_selected_unit] + u.inventory[ui_look_cursor].item if u and u.pos.z == cursor.z and + ui_unit_view_mode == :Inventory and u.inventory[ui_look_cursor] end end elsif what.kind_of?(Integer) From cb17bde8f47cc0e47c79d8dc33b38c5a8245a5e9 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 15:18:36 +0200 Subject: [PATCH 15/31] ruby: refix codegen for refers-to without key-field, add translate_name --- plugins/ruby/codegen.pl | 25 ++++++++++----- plugins/ruby/item.rb | 2 +- plugins/ruby/ruby-autogen-defs.rb | 2 +- plugins/ruby/ruby.rb | 51 +++++++++++++++++++++++++++++++ plugins/ruby/unit.rb | 6 ++++ 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index 9ec1eb788..cbe7c932a 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -298,7 +298,7 @@ sub render_field_reftarget { return if (!$tg); my $tgvec = $tg->getAttribute('instance-vector'); return if (!$tgvec); - my $idx = $tg->getAttribute('key-field') || 'id'; + my $idx = $tg->getAttribute('key-field'); $tgvec =~ s/^\$global/df/; return if $tgvec !~ /^[\w\.]+$/; @@ -310,9 +310,14 @@ sub render_field_reftarget { $tgname .= '_' if ($othername and $tgname eq $othername); } - my $fidx = ''; - $fidx = ', :' . $idx if ($idx ne 'id'); - push @lines_rb, "def $tgname ; ${tgvec}.binsearch($name$fidx) ; end"; + if ($idx) { + my $fidx = ''; + $fidx = ', :' . $idx if ($idx ne 'id'); + push @lines_rb, "def $tgname ; ${tgvec}.binsearch($name$fidx) ; end"; + } else { + push @lines_rb, "def $tgname ; ${tgvec}[$name] ; end"; + } + } sub render_field_refto { @@ -342,7 +347,7 @@ sub render_container_reftarget { return if (!$tg); my $tgvec = $tg->getAttribute('instance-vector'); return if (!$tgvec); - my $idx = $tg->getAttribute('key-field') || 'id'; + my $idx = $tg->getAttribute('key-field'); $tgvec =~ s/^\$global/df/; return if $tgvec !~ /^[\w\.]+$/; @@ -354,9 +359,13 @@ sub render_container_reftarget { $tgname .= '_' if ($othername and $tgname eq $othername); } - my $fidx = ''; - $fidx = ', :' . $idx if ($idx ne 'id'); - push @lines_rb, "def $tgname ; $name.map { |i| $tgvec.binsearch(i$fidx) } ; end"; + if ($idx) { + my $fidx = ''; + $fidx = ', :' . $idx if ($idx ne 'id'); + push @lines_rb, "def $tgname ; $name.map { |i| $tgvec.binsearch(i$fidx) } ; end"; + } else { + push @lines_rb, "def $tgname ; $name.map { |i| ${tgvec}[i] } ; end"; + } } sub render_class_vmethods { diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 5df1aed87..4acf45d60 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -18,7 +18,7 @@ module DFHack when :ViewUnits u = world.units.active[ui_selected_unit] u.inventory[ui_look_cursor].item if u and u.pos.z == cursor.z and - ui_unit_view_mode == :Inventory and u.inventory[ui_look_cursor] + ui_unit_view_mode.value == :Inventory and u.inventory[ui_look_cursor] end end elsif what.kind_of?(Integer) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 3c004b41a..945eebceb 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -87,7 +87,7 @@ module DFHack def compound(name=nil, &b) m = Class.new(Compound) DFHack.const_set(name, m) if name - m.instance_eval(&b) + m.class_eval(&b) m.new end def rtti_classname(n) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 64592e3eb..4c692c92c 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -85,6 +85,57 @@ module DFHack may = rawlist.find_all { |r| r.downcase.index(name.downcase) } may.first if may.length == 1 end + + def translate_name(name, english=true, onlylastpart=false) + out = [] + + if not onlylastpart + out << name.first_name if name.first_name != '' + if name.nickname != '' + case respond_to?(:d_init) && d_init.nickname_dwarf + when :REPLACE_ALL; return "`#{name.nickname}'" + when :REPLACE_FIRST; out.pop + end + out << "`#{name.nickname}'" + end + end + return out.join(' ') unless name.words.find { |w| w >= 0 } + + if not english + tsl = world.raws.language.translations[name.language] + if name.words[0] >= 0 or name.words[1] >= 0 + out << '' + out.last << tsl.words[name.words[0]] if name.words[0] >= 0 + out.last << tsl.words[name.words[1]] if name.words[1] >= 0 + end + if name.words[5] >= 0 + out << '' + (2..5).each { |i| out.last << tsl.words[name.words[i]] if name.words[i] >= 0 } + end + if name.words[6] >= 0 + out << tsl.words[name.words[6]] + end + else + wl = world.raws.language + if name.words[0] >= 0 or name.words[1] >= 0 + out << '' + out.last << wl.words[name.words[0]].forms[name.parts_of_speech[0]] if name.words[0] >= 0 + out.last << wl.words[name.words[1]].forms[name.parts_of_speech[1]] if name.words[1] >= 0 + end + if name.words[5] >= 0 + out << 'the ' + out.last.capitalize! if out.length == 1 + (2..5).each { |i| out.last << wl.words[name.words[i]].forms[name.parts_of_speech[i]] if name.words[i] >= 0 } + end + if name.words[6] >= 0 + out << 'of' + out.last.capitalize! if out.length == 1 + out << wl.words[name.words[6]].forms[name.parts_of_speech[6]] + end + end + + out.join(' ') + end end end diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index b2f23e25f..3b5cebc43 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -80,4 +80,10 @@ module DFHack list end end + + class LanguageName + def to_s(english=true) + df.translate_name(self, english) + end + end end From edf0a9cb3d2d0a1696566fc0795609f27f74ac55 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 17:24:03 +0200 Subject: [PATCH 16/31] ruby: add decode_mat method for mat_type/mat_index --- plugins/ruby/README | 8 ++ plugins/ruby/material.rb | 199 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 plugins/ruby/material.rb diff --git a/plugins/ruby/README b/plugins/ruby/README index 690e83ca0..a3f9bf945 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -106,6 +106,14 @@ See buildings.rb/buildbed for an exemple. df.each_tree(material) { |t| } Iterates over every tree of the given material (eg 'maple'). + df.translate_name(name, in_english=true, only_lastpart=false) +Decode the LanguageName structure as a String as displayed in the game UI. +A shortcut is available through name.to_s + + df.decode_mat(obj) +Returns a MaterialInfo definition for the given object, using its mat_type +and mat_index fields. Also works with a token string argument ('STONE:DOLOMITE') + DFHack callbacks ---------------- diff --git a/plugins/ruby/material.rb b/plugins/ruby/material.rb new file mode 100644 index 000000000..4a92118d6 --- /dev/null +++ b/plugins/ruby/material.rb @@ -0,0 +1,199 @@ +module DFHack + class MaterialInfo + attr_accessor :mat_type, :mat_index + attr_accessor :mode, :material, :creature, :figure, :plant, :inorganic + def initialize(what, idx=nil) + case what + when Integer + @mat_type, @mat_index = what, idx + decode_type_index + when String + decode_string(what) + else + @mat_type, @mat_index = what.mat_type, what.mat_index + decode_type_index + end + end + + CREATURE_BASE = 19 + FIGURE_BASE = CREATURE_BASE+200 + PLANT_BASE = FIGURE_BASE+200 + END_BASE = PLANT_BASE+200 + + # interpret the mat_type and mat_index fields + def decode_type_index + if @mat_index < 0 or @mat_type >= END_BASE + @mode = :Builtin + @material = df.world.raws.mat_table.builtin[@mat_type] + + elsif @mat_type >= PLANT_BASE + @mode = :Plant + @plant = df.world.raws.plants.all[@mat_index] + @material = @plant.material[@mat_type-PLANT_BASE] if @plant + + elsif @mat_type >= FIGURE_BASE + @mode = :Figure + @figure = df.world.history.figures.binsearch(@mat_index) + @creature = df.world.raws.creatures.all[@figure.race] if @figure + @material = @creature.material[@mat_type-FIGURE_BASE] if @creature + + elsif @mat_type >= CREATURE_BASE + @mode = :Creature + @creature = df.world.raws.creatures.all[@mat_index] + @material = @creature.material[@mat_type-CREATURE_BASE] if @creature + + elsif @mat_type > 0 + @mode = :Builtin + @material = df.world.raws.mat_table.builtin[@mat_type] + + elsif @mat_type == 0 + @mode = :Inorganic + @inorganic = df.world.raws.inorganics[@mat_index] + @material = @inorganic.material if @inorganic + end + end + + def decode_string(str) + parts = str.split(':') + case parts[0].chomp('_MAT') + when 'INORGANIC', 'STONE', 'METAL' + decode_string_inorganic(parts) + when 'PLANT' + decode_string_plant(parts) + when 'CREATURE' + if parts[3] and parts[3] != 'NONE' + decode_string_figure(parts) + else + decode_string_creature(parts) + end + when 'INVALID' + @mat_type = parts[1].to_i + @mat_index = parts[2].to_i + else + decode_string_builtin(parts) + end + end + + def decode_string_inorganic(parts) + @@inorganics_index ||= (0...df.world.raws.inorganics.length).inject({}) { |h, i| h.update df.world.raws.inorganics[i].id => i } + + @mode = :Inorganic + @mat_type = 0 + + if parts[1] and parts[1] != 'NONE' + @mat_index = @@inorganics_index[parts[1]] + raise "invalid inorganic token #{parts.join(':').inspect}" if not @mat_index + @inorganic = df.world.raws.inorganics[@mat_index] + @material = @inorganic.material + end + end + + def decode_string_builtin(parts) + @@builtins_index ||= (1...df.world.raws.mat_table.builtin.length).inject({}) { |h, i| b = df.world.raws.mat_table.builtin[i] ; b ? h.update(b.id => i) : h } + + @mode = :Builtin + @mat_index = -1 + @mat_type = @@builtins_index[parts[0]] + raise "invalid builtin token #{parts.join(':').inspect}" if not @mat_type + @material = df.world.raws.mat_table.builtin[@mat_type] + + if parts[0] == 'COAL' and parts[1] + @mat_index = ['COKE', 'CHARCOAL'].index(parts[1]) || -1 + end + end + + def decode_string_creature(parts) + @@creatures_index ||= (0...df.world.raws.creatures.all.length).inject({}) { |h, i| h.update df.world.raws.creatures.all[i].creature_id => i } + + @mode = :Creature + + if parts[1] and parts[1] != 'NONE' + @mat_index = @@creatures_index[parts[1]] + raise "invalid creature token #{parts.join(':').inspect}" if not @mat_index + @creature = df.world.raws.creatures.all[@mat_index] + end + + if @creature and parts[2] and parts[2] != 'NONE' + @mat_type = @creature.material.index { |m| m.id == parts[2] } + @material = @creature.material[@mat_type] + @mat_type += CREATURE_BASE + end + end + + def decode_string_figure(parts) + @mode = :Figure + @mat_index = parts[3].to_i + @figure = df.world.history.figures.binsearch(@mat_index) + raise "invalid creature histfig #{parts.join(':').inspect}" if not @figure + + @creature = df.world.raws.creatures.all[@figure.race] + if parts[1] and parts[1] != 'NONE' + raise "invalid histfig race #{parts.join(':').inspect}" if @creature.creature_id != parts[1] + end + + if @creature and parts[2] and parts[2] != 'NONE' + @mat_type = @creature.material.index { |m| m.id == parts[2] } + @material = @creature.material[@mat_type] + @mat_type += FIGURE_BASE + end + end + + def decode_string_plant(parts) + @@plants_index ||= (0...df.world.raws.plants.all.length).inject({}) { |h, i| h.update df.world.raws.plants.all[i].id => i } + + @mode = :Plant + + if parts[1] and parts[1] != 'NONE' + @mat_index = @@plants_index[parts[1]] + raise "invalid plant token #{parts.join(':').inspect}" if not @mat_index + @plant = df.world.raws.plants.all[@mat_index] + end + + if @plant and parts[2] and parts[2] != 'NONE' + @mat_type = @plant.material.index { |m| m.id == parts[2] } + raise "invalid plant type #{parts.join(':').inspect}" if not @mat_type + @material = @plant.material[@mat_type] + @mat_type += PLANT_BASE + end + end + + # delete the caches of raws id => index used in decode_string + def self.flush_raws_cache + @@inorganics_index = @@plants_index = @@creatures_index = @@builtins_index = nil + end + + def token + out = [] + case @mode + when :Builtin + out << (@material ? @material.id : 'NONE') + out << (['COKE', 'CHARCOAL'][@mat_index] || 'NONE') if @material and @material.id == 'COAL' and @mat_index >= 0 + when :Inorganic + out << 'INORGANIC' + out << @inorganic.id if @inorganic + when :Plant + out << 'PLANT_MAT' + out << @plant.id if @plant + out << @material.id if @plant and @material + when :Creature, :Figure + out << 'CREATURE_MAT' + out << @creature.creature_id if @creature + out << @material.id if @creature and @material + out << @figure.id.to_s if @creature and @material and @figure + else + out << 'INVALID' + out << @mat_type.to_s + out << @mat_index.to_s + end + out.join(':') + end + + def to_s ; token ; end + end + + class << self + def decode_mat(what, idx=nil) + MaterialInfo.new(what, idx) + end + end +end From 15cfc1d00b0ed1729a61171927deedd4e40c649b Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 17:24:46 +0200 Subject: [PATCH 17/31] ruby: handle constructed beds in item_find in LookAround mode --- plugins/ruby/item.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 4acf45d60..00f1ebf1f 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -11,7 +11,13 @@ module DFHack case ui.main.mode when :LookAround k = ui_look_list.items[ui_look_cursor] - k.item if k.type == :Item + case k.type + when :Item + k.item + when :Building + # hilight a constructed bed + k.building.contained_items[0].item if k.building.contained_items.length == 1 + end when :BuildingItems bld = world.selected_building bld.contained_items[ui_building_item_cursor].item if bld From cdd711efa4654bcf1c2385bdf3ed943ad26f0895 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 17:31:41 +0200 Subject: [PATCH 18/31] add COAL subtypes in MaterialInfo::getToken --- library/modules/Materials.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index f8f99f81e..50cf21a9c 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -293,6 +293,12 @@ std::string MaterialInfo::getToken() switch (mode) { case Builtin: + if (material->id == "COAL") { + if (index == 0) + return "COAL:COKE"; + else if (index == 1) + return "COAL:CHARCOAL"; + } return material->id; case Inorganic: return "INORGANIC:" + inorganic->id; From 7c4f60df1d84ce3d70b62e165b1e1045c1b4808d Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 17:48:44 +0200 Subject: [PATCH 19/31] ruby: extend item_find to work on constructed coffers --- plugins/ruby/item.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 00f1ebf1f..3b86f5e4f 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -15,8 +15,9 @@ module DFHack when :Item k.item when :Building - # hilight a constructed bed - k.building.contained_items[0].item if k.building.contained_items.length == 1 + # hilight a constructed bed/coffer + mats = k.building.contained_items.find_all { |i| i.use_mode == 2 } + mats[0].item if mats.length == 1 end when :BuildingItems bld = world.selected_building From f80ca239d02c5e130a4df918a4144912bcdef26c Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 18:34:41 +0200 Subject: [PATCH 20/31] ruby: allow coords arguments to *_find, add building_find --- plugins/ruby/building.rb | 40 ++++++++++++++++++++++++++++++++++++++++ plugins/ruby/item.rb | 8 ++++++-- plugins/ruby/unit.rb | 8 ++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 826cd26b9..9a59411f9 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -1,5 +1,45 @@ module DFHack class << self + def building_find(what=:selected, y=nil, z=nil) + if what == :selected + case ui.main.mode + when :LookAround + k = ui_look_list.items[ui_look_cursor] + k.building if k.type == :Building + when :BuildingItems, :QueryBuilding + world.selected_building + end + + elsif what.kind_of?(Integer) + # search by building.id + return world.buildings.all.binsearch(what) if not z + + # search by coordinates + x = what + world.buildings.all.find { |b| + b.z == z and + if b.room.extents + dx = x - b.room.x + dy = y - b.room.y + dx >= 0 and dx <= b.room.width and + dy >= 0 and dy <= b.room.height and + b.room.extents[ dy*b.room.width + dx ] > 0 + else + b.x1 <= x and b.x2 >= x and + b.y1 <= y and b.y2 >= y + end + } + + elsif what.respond_to?(:x) or what.respond_to?(:pos) + # find the building at the same position + what = what.pos if what.respond_to?(:pos) + building_find(what.x, what.y, what.z) + + else + raise "what what?" + end + end + # allocate a new building object def building_alloc(type, subtype=-1, custom=-1) cls = rtti_n2c[BuildingType::Classname[type].to_sym] diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 3b86f5e4f..a3fe68b97 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -2,7 +2,7 @@ module DFHack class << self # return an Item # arg similar to unit.rb/unit_find; no arg = 'k' menu - def item_find(what=:selected) + def item_find(what=:selected, y=nil, z=nil) if what == :selected if curview._rtti_classname == :viewscreen_itemst ref = curview.entry_ref[curview.cursor_pos] @@ -29,7 +29,11 @@ module DFHack end end elsif what.kind_of?(Integer) - world.items.all.binsearch(what) + # search by id + return world.items.all.binsearch(what) if not z + # search by position + x = what + world.items.all.find { |i| i.pos.x == x and i.pos.y == y and i.pos.z == z } elsif what.respond_to?(:x) or what.respond_to?(:pos) world.items.all.find { |i| same_pos?(what, i) } else diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index 3b5cebc43..04deee0e3 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -4,7 +4,7 @@ module DFHack # with no arg, return currently selected unit in df UI ('v' or 'k' menu) # with numeric arg, search unit by unit.id # with an argument that respond to x/y/z (eg cursor), find first unit at this position - def unit_find(what=:selected) + def unit_find(what=:selected, y=nil, z=nil) if what == :selected if curview._rtti_classname == :viewscreen_itemst ref = curview.entry_ref[curview.cursor_pos] @@ -21,7 +21,11 @@ module DFHack end end elsif what.kind_of?(Integer) - world.units.all.binsearch(what) + # search by id + return world.units.all.binsearch(what) if not z + # search by coords + x = what + world.units.all.find { |u| u.pos.x == x and u.pos.y == y and u.pos.z == z } elsif what.respond_to?(:x) or what.respond_to?(:pos) world.units.all.find { |u| same_pos?(what, u) } else From e4d4bf23ae0a919ba7b2d5575ff74a01f63bd808 Mon Sep 17 00:00:00 2001 From: jj Date: Wed, 4 Jul 2012 19:05:44 +0200 Subject: [PATCH 21/31] update xml, fix autolabor with new trade depot flags name --- library/xml | 2 +- plugins/autolabor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index ad38c5e96..eb6f706d7 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ad38c5e96b05fedf16114fd16bd463e933f13582 +Subproject commit eb6f706d702367ea3121272670e603000bbdd42a diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index de1a1aef6..3bae1e1b3 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -740,7 +740,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) else if (df::enums::building_type::TradeDepot == type) { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; - trader_requested = depot->flags.bits.trader_requested; + trader_requested = depot->trade_flags.bits.trader_requested; if (print_debug) out.print("Trade depot found and trader requested, trader will be excluded from all labors.\n"); } From f560d2de1137940942c3b9cb926a0093359e80fa Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Jul 2012 14:15:34 +0200 Subject: [PATCH 22/31] ruby: add MapTile class --- plugins/ruby/README | 3 ++ plugins/ruby/item.rb | 2 +- plugins/ruby/map.rb | 112 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index a3f9bf945..493e3c821 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -66,6 +66,9 @@ obj1 and 2 should respond to #pos and #x #y #z. df.map_block_at(pos) / map_block_at(x, y, z) Returns the MapBlock for the coordinates or nil. + df.map_tile_at(pos) +Returns a MapTile, holds all information relative to the map tile. + df.each_map_block { |b| } df.each_map_block_z(zlevel) { |b| } Iterates over every map block (opt. on a single z-level). diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index a3fe68b97..34b404505 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -33,7 +33,7 @@ module DFHack return world.items.all.binsearch(what) if not z # search by position x = what - world.items.all.find { |i| i.pos.x == x and i.pos.y == y and i.pos.z == z } + world.items.all.find { |i| i.pos.x == x and i.pos.y == y and i.pos.z == z } elsif what.respond_to?(:x) or what.respond_to?(:pos) world.items.all.find { |i| same_pos?(what, i) } else diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index af9e8b804..a0438670d 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -26,6 +26,13 @@ module DFHack end end + def map_tile_at(x, y=nil, z=nil) + x = x.pos if x.respond_to?(:pos) + x, y, z = x.x, x.y, x.z if x.respond_to?(:x) + b = map_block_at(x, y, z) + MapTile.new(b, x, y, z) if b + end + # yields every map block def each_map_block (0...world.map.x_count_block).each { |xb| @@ -51,4 +58,109 @@ module DFHack } end end + + class MapTile + attr_accessor :x, :y, :z, :dx, :dy, :mapblock + def initialize(b, x, y, z) + @x, @y, @z = x, y, z + @dx, @dy = @x&15, @y&15 + @mapblock = b + end + + def designation + @mapblock.designation[@dx][@dy] + end + + def occupancy + @mapblock.occupancy[@dx][@dy] + end + + def tiletype + @mapblock.tiletype[@dx][@dy] + end + + def tiletype=(t) + @mapblock.tiletype[@dx][@dy] = t + end + + def caption + Tiletype::Caption[tiletype] + end + + def shape + Tiletype::Shape[tiletype] + end + + def tilemat + Tiletype::Material[tiletype] + end + + def variant + Tiletype::Variant[tiletype] + end + + def special + Tiletype::Special[tiletype] + end + + def direction + Tiletype::Direction[tiletype] + end + + # return all veins for current mapblock + def all_veins + mapblock.block_events.grep(BlockSquareEventMineralst) + end + + # return the vein applicable to current tile + def vein + # last vein wins + all_veins.reverse.find { |v| + (v.tile_bitmask.bits[@dy] & (1 << @dx)) > 0 + } + end + + # return the mat_index for the current tile (if in vein) + def mat_index_vein + v = vein + v.inorganic_mat if v + end + + # return the world_data.geo_biome for current tile + def geo_biome + b = designation.biome + wd = df.world.world_data + + # region coords + [[-1, -1], [0, -1], ..., [1, 1]][b] + # clipped to world dimensions + rx = df.world.map.region_x/16 + rx -= 1 if b % 3 == 0 and rx > 0 + rx += 1 if b % 3 == 2 and rx < wd.world_width-1 + + ry = df.world.map.region_y/16 + ry -= 1 if b < 3 and ry > 0 + ry += 1 if b > 5 and ry < wd.world_height-1 + + wd.geo_biomes[ wd.region_map[rx][ry].geo_index ] + end + + # return the world_data.geo_biome.layer for current tile + def stone_layer + geo_biome.layers[designation.geolayer_index] + end + + # current tile mat_index (vein if applicable, or base material) + def mat_index + mat_index_vein or stone_layer.mat_index + end + + # MaterialInfo: inorganic token for current tile + def mat_info + MaterialInfo.new(0, mat_index) + end + + def inspect + "#" + end + end end From 7732901b6ec1af9ed5e77a118bade7285da3e018 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Jul 2012 15:35:37 +0200 Subject: [PATCH 23/31] ruby: tweak onupdate to allow tick rate-limiting --- plugins/ruby/README | 4 +++ plugins/ruby/ruby.cpp | 45 +++++++++++++++++++++++++++----- plugins/ruby/ruby.rb | 60 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 11 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index 493e3c821..97a4cbd30 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -124,6 +124,10 @@ DFHack callbacks The plugin interfaces with dfhack 'onupdate' hook. To register ruby code to be run every graphic frame, use: handle = df.onupdate_register { puts 'i love flooding the console' } +You can also rate-limit when your callback is called to a number of game ticks: + handle = df.onupdate_register(10) { puts '10 more in-game ticks elapsed' } +In this case, the callback is called immediately, and then every X in-game +ticks (advances only when the game is unpaused). To stop being called, use: df.onupdate_unregister handle diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index f1f007263..6dd0b39c8 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -6,8 +6,7 @@ #include "VersionInfo.h" #include "DataDefs.h" -#include "df/world.h" -#include "df/unit.h" +#include "df/global_objects.h" #include "tinythread.h" @@ -39,6 +38,7 @@ static color_ostream *r_console; // color_ostream given as argument, if NU static const char *r_command; static tthread::thread *r_thread; static int onupdate_active; +static int onupdate_minyear, onupdate_minyeartick; static color_ostream_proxy *console_proxy; @@ -165,10 +165,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) // ruby sets this flag when needed, to avoid lag running ruby code every // frame if not necessary - // TODO dynamic check on df::cur_year{_tick} if (!onupdate_active) return CR_OK; + if (*df::global::cur_year < onupdate_minyear) + return CR_OK; + if (*df::global::cur_year == onupdate_minyear && + *df::global::cur_year_tick < onupdate_minyeartick) + return CR_OK; + return plugin_eval_ruby(out, "DFHack.onupdate"); } @@ -422,7 +427,7 @@ static VALUE rb_cDFHack; // DFHack module ruby methods, binds specific dfhack methods // enable/disable calls to DFHack.onupdate() -static VALUE rb_dfonupdateactive(VALUE self) +static VALUE rb_dfonupdate_active(VALUE self) { if (onupdate_active) return Qtrue; @@ -430,12 +435,34 @@ static VALUE rb_dfonupdateactive(VALUE self) return Qfalse; } -static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val) +static VALUE rb_dfonupdate_active_set(VALUE self, VALUE val) { onupdate_active = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } +static VALUE rb_dfonupdate_minyear(VALUE self) +{ + return rb_uint2inum(onupdate_minyear); +} + +static VALUE rb_dfonupdate_minyear_set(VALUE self, VALUE val) +{ + onupdate_minyear = rb_num2ulong(val); + return Qtrue; +} + +static VALUE rb_dfonupdate_minyeartick(VALUE self) +{ + return rb_uint2inum(onupdate_minyeartick); +} + +static VALUE rb_dfonupdate_minyeartick_set(VALUE self, VALUE val) +{ + onupdate_minyeartick = rb_num2ulong(val); + return Qtrue; +} + static VALUE rb_dfprint_str(VALUE self, VALUE s) { if (r_console) @@ -777,8 +804,12 @@ static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE static void ruby_bind_dfhack(void) { rb_cDFHack = rb_define_module("DFHack"); - rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdateactive), 0); - rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdateactiveset), 1); + rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdate_active), 0); + rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdate_active_set), 1); + rb_define_singleton_method(rb_cDFHack, "onupdate_minyear", RUBY_METHOD_FUNC(rb_dfonupdate_minyear), 0); + rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1); + rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0); + rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1); rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 4c692c92c..eda6ffd52 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -23,26 +23,78 @@ module Kernel end module DFHack + class OnupdateCallback + attr_accessor :callback, :timelimit, :minyear, :minyeartick + def initialize(cb, tl) + @callback = cb + @ticklimit = tl + @minyear = (tl ? df.cur_year : 0) + @minyeartick = (tl ? df.cur_year_tick : 0) + end + + # run callback if timedout + def check_run(year, yeartick, yearlen) + if !@ticklimit + @callback.call + else + if year > @minyear or (year == @minyear and yeartick >= @minyeartick) + @callback.call + @minyear = year + @minyeartick = yeartick + @ticklimit + if @minyeartick > yearlen + @minyear += 1 + @minyeartick -= yearlen + end + end + end + end + + def <=>(o) + [@minyear, @minyeartick] <=> [o.minyear, o.minyeartick] + end + end + class << self + attr_accessor :onupdate_list, :onstatechange_list + # register a callback to be called every gframe or more # ex: DFHack.onupdate_register { DFHack.world.units[0].counters.job_counter = 0 } - def onupdate_register(&b) + def onupdate_register(ticklimit=nil, &b) @onupdate_list ||= [] - @onupdate_list << b + @onupdate_list << OnupdateCallback.new(b, ticklimit) DFHack.onupdate_active = true + if onext = @onupdate_list.sort.first + DFHack.onupdate_minyear = onext.minyear + DFHack.onupdate_minyeartick = onext.minyeartick + end @onupdate_list.last end # delete the callback for onupdate ; use the value returned by onupdate_register def onupdate_unregister(b) @onupdate_list.delete b - DFHack.onupdate_active = false if @onupdate_list.empty? + if @onupdate_list.empty? + DFHack.onupdate_active = false + DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = 0 + end end + TICKS_PER_YEAR = 1200*28*12 # this method is called by dfhack every 'onupdate' if onupdate_active is true def onupdate @onupdate_list ||= [] - @onupdate_list.each { |cb| cb.call } + + ticks_per_year = TICKS_PER_YEAR + ticks_per_year *= 72 if gametype == :ADVENTURE_MAIN or gametype == :ADVENTURE_ARENA + + @onupdate_list.each { |o| + o.check_run(cur_year, cur_year_tick, ticks_per_year) + } + + if onext = @onupdate_list.sort.first + DFHack.onupdate_minyear = onext.minyear + DFHack.onupdate_minyeartick = onext.minyeartick + end end # register a callback to be called every gframe or more From 70740481463bbde3f078b612e529e6c7a3b6704d Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Jul 2012 16:17:41 +0200 Subject: [PATCH 24/31] showmood: display number of items already collected --- plugins/ruby/ruby.rb | 2 +- plugins/showmood.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index eda6ffd52..5ae63ebfe 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -66,7 +66,7 @@ module DFHack if onext = @onupdate_list.sort.first DFHack.onupdate_minyear = onext.minyear DFHack.onupdate_minyeartick = onext.minyeartick - end + end @onupdate_list.last end diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 7926e2ac5..10d7b52c2 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -165,6 +165,8 @@ command_result df_showmood (color_ostream &out, vector & parameters) out.print("not yet claimed a workshop but will want"); out.print(" the following items:\n"); + int count_got = job->items.size(), got; + for (size_t i = 0; i < job->job_items.size(); i++) { df::job_item *item = job->job_items[i]; @@ -267,7 +269,11 @@ command_result df_showmood (color_ostream &out, vector & parameters) } } - out.print(", quantity %i\n", item->quantity); + got = count_got; + if (got > item->quantity) + got = item->quantity; + out.print(", quantity %i (got %i)\n", item->quantity, got); + count_got -= got; } } if (!found) From 8e17ebbefc5da79b34219b87b310038af1077871 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 5 Jul 2012 18:03:02 +0200 Subject: [PATCH 25/31] add SC_PAUSED / SC_UNPAUSED onStateChange events --- library/Core.cpp | 10 ++++++++++ library/include/Core.h | 5 ++++- library/lua/dfhack.lua | 2 ++ plugins/ruby/ruby.cpp | 2 ++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 59334906d..826576b77 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -752,6 +752,7 @@ Core::Core() misc_data_mutex=0; last_world_data_ptr = NULL; last_local_map_ptr = NULL; + last_pause_state = false; top_viewscreen = NULL; screen_window = NULL; server = NULL; @@ -1116,6 +1117,15 @@ int Core::Update() } } + if (df::global::pause_state) + { + if (*df::global::pause_state != last_pause_state) + { + onStateChange(out, last_pause_state ? SC_UNPAUSED : SC_PAUSED); + last_pause_state = *df::global::pause_state; + } + } + // Execute per-frame handlers onUpdate(out); diff --git a/library/include/Core.h b/library/include/Core.h index e4d1080d6..0d5d3150e 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -75,7 +75,9 @@ namespace DFHack SC_MAP_UNLOADED = 3, SC_VIEWSCREEN_CHANGED = 4, SC_CORE_INITIALIZED = 5, - SC_BEGIN_UNLOAD = 6 + SC_BEGIN_UNLOAD = 6, + SC_PAUSED = 7, + SC_UNPAUSED = 8 }; // Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF. @@ -221,6 +223,7 @@ namespace DFHack // for state change tracking void *last_local_map_ptr; df::viewscreen *top_viewscreen; + bool last_pause_state; // Very important! bool started; diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index d56d4df60..86ea1459f 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -39,6 +39,8 @@ if dfhack.is_core_context then SC_MAP_UNLOADED = 3 SC_VIEWSCREEN_CHANGED = 4 SC_CORE_INITIALIZED = 5 + SC_PAUSED = 7 + SC_UNPAUSED = 8 end -- Error handling diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 6dd0b39c8..08ea13b9f 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -194,6 +194,8 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch // if we go through plugin_eval at BEGIN_UNLOAD, it'll // try to get the suspend lock and deadlock at df exit case SC_BEGIN_UNLOAD : return CR_OK; + SCASE(PAUSED); + SCASE(UNPAUSED); #undef SCASE } From fb8f0510a4b8c1760e44fcbe35d04d9b03f709df Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Jul 2012 13:00:38 +0200 Subject: [PATCH 26/31] ruby: update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 81b55ffdb..1fc1acae0 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,10 @@ dfhack/python/PyDFHack.egg-info dfhack/python/build dfhack/python/dist +# Ruby binding binaries +plugins/ruby/libruby* +plugins/ruby/ruby-autogen.rb + # CPack stuff build/CPack*Config.cmake From 1c5b5b956f1a92bd25f7bab544c463fbff9d8f4a Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Jul 2012 13:01:53 +0200 Subject: [PATCH 27/31] gitignore build/install_manifest --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1fc1acae0..ceb0aa27a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ build/library build/tools build/plugins build/depends +build/install_manifest.txt #ignore Kdevelop stuff .kdev4 From bef5079d436c70be897d9b25e9bfe12ffa9d0eea Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Jul 2012 13:25:47 +0200 Subject: [PATCH 28/31] ruby: add vector.first/last --- plugins/ruby/ruby-autogen-defs.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 945eebceb..f9858c949 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -354,7 +354,9 @@ module DFHack end def empty? ; length == 0 ; end def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end - def index(elem=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == elem } ; end + def index(e=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == e } ; end + def first ; self[0] ; end + def last ; self[length-1] ; end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_indexenum, :_tg From d645d6b0465e4a280aa975e9a78b3508d316ce2f Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Jul 2012 13:32:39 +0200 Subject: [PATCH 29/31] ruby: better message on out of bounds array access --- plugins/ruby/ruby-autogen-defs.rb | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index f9858c949..a1cba4168 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -380,12 +380,18 @@ module DFHack def [](i) i = _indexenum.int(i) if _indexenum i += @_length if i < 0 - _tgat(i)._get + if t = _tgat(i) + t._get + end end def []=(i, v) i = _indexenum.int(i) if _indexenum i += @_length if i < 0 - _tgat(i)._set(v) + if t = _tgat(i) + t._set(v) + else + raise 'index out of bounds' + end end include Enumerable @@ -444,7 +450,7 @@ module DFHack if idx >= length insert_at(idx, 0) elsif idx < 0 - raise 'invalid idx' + raise 'index out of bounds' end @_tg._at(valueptr_at(idx))._set(v) end @@ -530,7 +536,7 @@ module DFHack if idx >= length insert_at(idx, v) elsif idx < 0 - raise 'invalid idx' + raise 'index out of bounds' else DFHack.memory_vectorbool_setat(@_memaddr, idx, v) end @@ -582,7 +588,7 @@ module DFHack idx = _indexenum.int(idx) if _indexenum idx += length if idx < 0 if idx >= length or idx < 0 - raise 'invalid idx' + raise 'index out of bounds' else DFHack.memory_bitarray_set(@_memaddr, idx, v) end @@ -608,11 +614,17 @@ module DFHack end def [](i) i += _length if i < 0 - _tgat(i)._get + if t = _tgat(i) + t._get + end end def []=(i, v) i += _length if i < 0 - _tgat(i)._set(v) + if t = _tgat(i) + t._set(v) + else + raise 'index out of bounds' + end end def _set(a) a.each_with_index { |v, i| self[i] = v } From 4bbb185f7ba546fbab44e275fa243abb764e7571 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 6 Jul 2012 20:11:20 +0400 Subject: [PATCH 30/31] Fix build and allow autodump to any walkable tile (e.g. a stair). --- library/xml | 2 +- plugins/autodump.cpp | 2 +- plugins/autolabor.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/xml b/library/xml index ad38c5e96..b646637f8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ad38c5e96b05fedf16114fd16bd463e933f13582 +Subproject commit b646637f8eb901a95c82e60ccd4713e763e00179 diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 536b2501b..cfb73fa8b 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -140,7 +140,7 @@ static command_result autodump_main(color_ostream &out, vector & parame return CR_FAILURE; } df::tiletype ttype = MC.tiletypeAt(pos_cursor); - if(!DFHack::isFloorTerrain(ttype)) + if(!DFHack::isWalkable(ttype) || DFHack::isOpenTerrain(ttype)) { out.printerr("Cursor should be placed over a floor.\n"); return CR_FAILURE; diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index de1a1aef6..3bae1e1b3 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -740,7 +740,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) else if (df::enums::building_type::TradeDepot == type) { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; - trader_requested = depot->flags.bits.trader_requested; + trader_requested = depot->trade_flags.bits.trader_requested; if (print_debug) out.print("Trade depot found and trader requested, trader will be excluded from all labors.\n"); } From 028b47a3213ef951690a1006dc7f9ad60f03917f Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 6 Jul 2012 20:36:53 +0200 Subject: [PATCH 31/31] update xml --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index eb6f706d7..b646637f8 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit eb6f706d702367ea3121272670e603000bbdd42a +Subproject commit b646637f8eb901a95c82e60ccd4713e763e00179