v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

Difference between revisions of "Utility:DFusion"

From Dwarf Fortress Wiki
Jump to navigation Jump to search
m ({{uv}})
 
(24 intermediate revisions by 7 users not shown)
Line 1: Line 1:
 
+
{{uv|DF2012}}
 +
WARNING: this page is very outdated and has not been maintained since early 2012.
 
== DFusion info page ==
 
== DFusion info page ==
 
Thread for discussions: [http://www.bay12forums.com/smf/index.php?topic=93317.0 Thread]
 
Thread for discussions: [http://www.bay12forums.com/smf/index.php?topic=93317.0 Thread]
 
== Usage ==
 
== Usage ==
Dfusion plugin offers two DFhack commands: 'dfusion' and 'lua'.
+
Dfusion plugin offers four DFhack commands: 'dfusion', 'dfuse' and 'lua', 'runlua'.
 
=== lua ===
 
=== lua ===
 
Runs an interactive lua console. For more on lua commands see [http://www.lua.org/manual/5.1/manual.html Lua reference manual] or google "lua". Also this command could be ran with filepath as an argument. Then it runs that file as a lua script file. E.g. ''lua dfusion/temp.lua'' runs a file  <your df path>/dfusion/temp.lua.
 
Runs an interactive lua console. For more on lua commands see [http://www.lua.org/manual/5.1/manual.html Lua reference manual] or google "lua". Also this command could be ran with filepath as an argument. Then it runs that file as a lua script file. E.g. ''lua dfusion/temp.lua'' runs a file  <your df path>/dfusion/temp.lua.
 +
=== runlua ===
 +
Similar to ''lua <filename>'' but not interactive, to be used with hotkeys
 
=== dfusion ===
 
=== dfusion ===
 
First this command runs all plugins' init.lua part then show a menu. Type number to run specified plugin.
 
First this command runs all plugins' init.lua part then show a menu. Type number to run specified plugin.
 +
 +
Note: You may see the following error when running dfusion in DFHack: "dfusion/common.lua:45: . Text region not found!"
 +
 +
You just have to do an easy text change to fix it.  Find this line in DF/dfusion/common.lua (it's near the top):
 +
local pos=string.find(v.name,".text") or string.find(v.name,"libs/Dwarf_Fortress")
 +
 +
Then, replace ".text" with "Dwarf Fortress.exe" and save.
 +
 +
=== dfuse ===
 +
Similar to dfusion but not interactive. To be used with hotkeys (later will have command support).
 +
 +
 +
Also dfuse/dfusion runs an init script located at 'save directory/dfusion/init.lua'. And 'initcustom.lua' if it exists
 +
 +
== Exported functions and objects ==
 +
The DF is accessed by object 'df'. This object holds all the types, globals table and some helper functions.
 +
Up-to-date api help could be found here: https://github.com/peterix/dfhack/blob/master/LUA_API.rst
 +
=== Examples ===
 +
<pre>
 +
first_unit=df.global.world.units.all[0] --gets first unit
 +
first_unit.name.first_name="Urist"
 +
</pre>
 +
You can iterate in all the objects:
 +
<pre>
 +
for k,v in pairs(df.global.world.units.all) do --gets ALL the units
 +
  v.name.first_name="Urist" --now everyone will be called URIST!!
 +
end
 +
</pre>
 +
Also all the flags can be accessed by name or by number:
 +
<pre>
 +
first_unit.flags1.dead=true --is same as...
 +
first_unit.flags1[1]=true --... this
 +
</pre>
 +
For convienence there is function printall:
 +
<pre>
 +
printall(df.global.world.units.all) --prints all the units, with locations in memory
 +
</pre>
 +
 +
=== Other objects ===
 +
==== Console. ====
 +
{| class="wikitable"
 +
|-
 +
! name
 +
! description
 +
! example
 +
|-
 +
| print
 +
| prints a message to dfhack console
 +
| Console.print("Hello world!")
 +
|-
 +
| printerr
 +
| prints an error message to dfhack console
 +
| Console.printerr("Error  world!")
 +
|-
 +
| clear
 +
| clears console
 +
| Console.clear()
 +
|-
 +
| color
 +
| sets text color
 +
| Console.color(2)
 +
|-
 +
| reset_color
 +
| resets text color to defaults
 +
| Console.reset_color()
 +
|-
 +
| cursor
 +
| sets if the cursor should be visible
 +
| Console.cursor(true)
 +
|-
 +
| msleep
 +
| waits for x milliseconds
 +
| Console.msleep(200)
 +
|-
 +
| get_columns
 +
| returns number of columns in the console
 +
| Console.get_columns()
 +
|-
 +
| get_rows
 +
| returns number of rows in the console
 +
| Console.get_rows()
 +
|-
 +
| lineedit
 +
| asks user for input
 +
| Console.lineedit("Type in your name:")
 +
|}
 +
==== Process. ====
 +
==== VersionInfo. ====
 +
==== engine. ====
 +
{| class="wikitable"
 +
|-
 +
! name
 +
! description
 +
! example
 +
|-
 +
| peek(b\w\d)
 +
| reads byte\word\double word from memory
 +
| peekb(0x15486)
 +
|-
 +
| peekarb
 +
| reads a chunk of memory
 +
| peekarb(0x15486,100) -- reads 100 bytes from 0x15486
 +
|-
 +
| peekstr
 +
| reads a string from memory
 +
| peekstr(0x15486)
 +
|-
 +
| poke(b\w\d)
 +
| writes byte\word\double word to memory
 +
| peekb(0x15486,100)
 +
|-
 +
| pokearb
 +
| writes a chunk of memory (gotten with peekarb)
 +
| pokearb(0x15486,data,100) -- writes 100 bytes from data to 0x15486
 +
|-
 +
| pokestr
 +
| writes a string to memory
 +
| pokestr(0x15486,"Hello world")
 +
|}
 +
 +
==== FunctionCall.====
 +
{| class="wikitable"
 +
|-
 +
! name
 +
! description
 +
! example
 +
|-
 +
| call(f_ptr,calling_convention,arguments...)
 +
| calls a function from df. Supports up to 7 arguments. Danger: this could (and probably will) crash df if used incorrectly.
 +
| FunctionCall.call(somepointertofunction,FunctionCall.THIS_CALL,thisptr)
 +
|-
 +
| THIS_CALL
 +
| a numeric constant for function calling convention.
 +
| -
 +
|-
 +
| STD_CALL
 +
| -"-
 +
| -
 +
|-
 +
| FAST_CALL
 +
| -"-
 +
| -
 +
|-
 +
| CDECL_CALL
 +
| -"-
 +
| -
 +
|}
 +
 +
=== From scripts ===
 +
These commands are exported from script files. Most of them are in "common.lua" file.
 +
=== Patterns ===
 +
DEPRECATED use df.<subitem>
 +
 
== How to ==
 
== How to ==
 
This section explains in detail some of more complex things that could be done with dfusion.
 
This section explains in detail some of more complex things that could be done with dfusion.
=== OnFunction ===
 
There are two parts to OnFunction: adding new triggers and using already existing triggers. To add new trigger you must know exact location of function call (and what it does) also it would help a lot if you know what registers correspond to what data. Usually this could be worked out by analysing call stack after setting a data breakpoint (watch in GDB) and then guessing what does what.
 
Using already existing triggers is way simpler. Adding a callback (a function to be called when a trigger is encountered) is done by ''onfunction.SetCallback(name,function)''. For possible names see locations.lua (at the time of writing there is "Move" and "Die" both in Linux and Windows). Possible use of it:
 
  
 +
Individual units can be modified - skills, jobs, attributes, etc. This example selects a unit under the look cursor, updates the name and updates the level and experience for selected skills.
 
<pre>
 
<pre>
function DeathMsg(values)
+
--Takes a selected unit and instantly turns them into Morul - upgrading all appropriate skills to Legendary or higher, as appropriate
local name
+
--By Martin, 5/25/2012, excerpt for demonstration purposes
name=engine.peek(values[onfunction.hints["Die"].creature],ptt_dfstring)
+
--This code gets cut/pasted at the end of /dfusion/tools/init.lua
print(name:getval().." died")
+
function tools.morul(unit)
 +
  --include the utils library which we need later
 +
  utils = require 'utils'
 +
  --a unit address can be passed into this function, handle the case where one isn't
 +
  if unit==nil then
 +
    unit=getCreatureAtPointer() --place the x pointer on top of the creature you want to change. best use is the look pointer 'k' and not 'v'
 +
  end
 +
  --Set the name. Surname is comprised of an array of references [words] into the language file. Each entry grabs a word fragment and the name is all of these catenated
 +
  unit.name.first_name = "Morul"
 +
  unit.name.words[0] = 1416  --[T_WORD:CHANNEL:catten]
 +
  unit.name.words[1] = 1304  --[T_WORD:BEND:mat]
 +
 
 +
  --skill level we wish to apply to all labors. Dabbling is 0, Legendary is 15
 +
  labor_rating = 15
 +
  --the skill level we wish to apply to all military skills.
 +
  military_rating = 70
 +
 
 +
  --These are the skill ids we want to add. The ids were identified in game_data.ini in DwarfTherapist.
 +
  skill = { 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 }
 +
  --These are the military skills that will get higher values. Subset of the list above.
 +
  military = { 38,39,41,42,43,44,45,46,54,99,100,101,102,103,104,105 }
 +
 
 +
  --ipairs iterates over each indexed item in the skill list. Lua index is 1 based, so remember that.
 +
  for sk,sv in ipairs(skill) do
 +
    --assume the labor rating will apply
 +
    new_rating = labor_rating
 +
    --iterate over the military list
 +
    for _,v in ipairs(military) do
 +
      --if the value from the skill list iteration is in the military list...
 +
      if v == sv then
 +
        --then use the military skill level
 +
        new_rating = military_rating
 +
      end
 +
    end
 +
    --the skills need to be inserted in a particular way. This standard function passes the id, the skill level, and the experience value, and tells the tool to keep the skills sorted by id.
 +
    --the experience calculation reflects the minimum experience to earn the level and allows skills to continue to advance.
 +
    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
 +
 
 +
  -- walk over each of the skills we just set and print them out so we can confirm each update
 +
  for sk,sv in ipairs(unit.status.current_soul.skills) do
 +
    -- print the skill data. Printall will print entire tables and is handy at poking at the data structures
 +
    printall(sv)
 +
  end
 
end
 
end
onfunction.SetCallback("Die",DeathMsg)
+
 
 +
--add the function above to the dfusion menu system as a submenu under the 'Tools' menu.
 +
tools.menu:add("Morul",tools.morul)
 
</pre>
 
</pre>
In this example we bind 'DeathMsg' function to "Die" trigger. Values argument has all the registers and a return value. So we lookup from witch register do we need to read the creature pointer ( ''values[onfunction.hints["Die"].creature'' ) and read a string just from the beginning (usually the creature starts with his name).
 
  
Note: there can be only one callback per trigger. Also if you know any more trigger locations please share :)
+
== Tips and Tricks ==
 +
* To use dfusion's functionality in lua first run ''dfuse'' and then use run ''lua''
 +
* You can bind keys to lua commands (in dfusion context) like this: ''keybindings add Shift-R "dfuse adv_tools.reincarnate()"''

Latest revision as of 21:35, 12 June 2014

This utility page is about v0.34.11, an older version of DF.

WARNING: this page is very outdated and has not been maintained since early 2012.

DFusion info page[edit]

Thread for discussions: Thread

Usage[edit]

Dfusion plugin offers four DFhack commands: 'dfusion', 'dfuse' and 'lua', 'runlua'.

lua[edit]

Runs an interactive lua console. For more on lua commands see Lua reference manual or google "lua". Also this command could be ran with filepath as an argument. Then it runs that file as a lua script file. E.g. lua dfusion/temp.lua runs a file <your df path>/dfusion/temp.lua.

runlua[edit]

Similar to lua <filename> but not interactive, to be used with hotkeys

dfusion[edit]

First this command runs all plugins' init.lua part then show a menu. Type number to run specified plugin.

Note: You may see the following error when running dfusion in DFHack: "dfusion/common.lua:45: . Text region not found!"

You just have to do an easy text change to fix it. Find this line in DF/dfusion/common.lua (it's near the top): local pos=string.find(v.name,".text") or string.find(v.name,"libs/Dwarf_Fortress")

Then, replace ".text" with "Dwarf Fortress.exe" and save.

dfuse[edit]

Similar to dfusion but not interactive. To be used with hotkeys (later will have command support).


Also dfuse/dfusion runs an init script located at 'save directory/dfusion/init.lua'. And 'initcustom.lua' if it exists

Exported functions and objects[edit]

The DF is accessed by object 'df'. This object holds all the types, globals table and some helper functions. Up-to-date api help could be found here: https://github.com/peterix/dfhack/blob/master/LUA_API.rst

Examples[edit]

first_unit=df.global.world.units.all[0] --gets first unit
first_unit.name.first_name="Urist"

You can iterate in all the objects:

for k,v in pairs(df.global.world.units.all) do --gets ALL the units
   v.name.first_name="Urist" --now everyone will be called URIST!!
end

Also all the flags can be accessed by name or by number:

first_unit.flags1.dead=true --is same as...
first_unit.flags1[1]=true --... this

For convienence there is function printall:

printall(df.global.world.units.all) --prints all the units, with locations in memory

Other objects[edit]

Console.[edit]

name description example
print prints a message to dfhack console Console.print("Hello world!")
printerr prints an error message to dfhack console Console.printerr("Error world!")
clear clears console Console.clear()
color sets text color Console.color(2)
reset_color resets text color to defaults Console.reset_color()
cursor sets if the cursor should be visible Console.cursor(true)
msleep waits for x milliseconds Console.msleep(200)
get_columns returns number of columns in the console Console.get_columns()
get_rows returns number of rows in the console Console.get_rows()
lineedit asks user for input Console.lineedit("Type in your name:")

Process.[edit]

VersionInfo.[edit]

engine.[edit]

name description example
peek(b\w\d) reads byte\word\double word from memory peekb(0x15486)
peekarb reads a chunk of memory peekarb(0x15486,100) -- reads 100 bytes from 0x15486
peekstr reads a string from memory peekstr(0x15486)
poke(b\w\d) writes byte\word\double word to memory peekb(0x15486,100)
pokearb writes a chunk of memory (gotten with peekarb) pokearb(0x15486,data,100) -- writes 100 bytes from data to 0x15486
pokestr writes a string to memory pokestr(0x15486,"Hello world")

FunctionCall.[edit]

name description example
call(f_ptr,calling_convention,arguments...) calls a function from df. Supports up to 7 arguments. Danger: this could (and probably will) crash df if used incorrectly. FunctionCall.call(somepointertofunction,FunctionCall.THIS_CALL,thisptr)
THIS_CALL a numeric constant for function calling convention. -
STD_CALL -"- -
FAST_CALL -"- -
CDECL_CALL -"- -

From scripts[edit]

These commands are exported from script files. Most of them are in "common.lua" file.

Patterns[edit]

DEPRECATED use df.<subitem>

How to[edit]

This section explains in detail some of more complex things that could be done with dfusion.

Individual units can be modified - skills, jobs, attributes, etc. This example selects a unit under the look cursor, updates the name and updates the level and experience for selected skills.

--Takes a selected unit and instantly turns them into Morul - upgrading all appropriate skills to Legendary or higher, as appropriate
--By Martin, 5/25/2012, excerpt for demonstration purposes
--This code gets cut/pasted at the end of /dfusion/tools/init.lua
function tools.morul(unit)
  --include the utils library which we need later
  utils = require 'utils'
  --a unit address can be passed into this function, handle the case where one isn't
  if unit==nil then
    unit=getCreatureAtPointer()  --place the x pointer on top of the creature you want to change. best use is the look pointer 'k' and not 'v'
  end
  --Set the name. Surname is comprised of an array of references [words] into the language file. Each entry grabs a word fragment and the name is all of these catenated 
  unit.name.first_name = "Morul"
  unit.name.words[0] = 1416  --[T_WORD:CHANNEL:catten]
  unit.name.words[1] = 1304  --[T_WORD:BEND:mat]

  --skill level we wish to apply to all labors. Dabbling is 0, Legendary is 15
  labor_rating = 15
  --the skill level we wish to apply to all military skills. 
  military_rating = 70
  
  --These are the skill ids we want to add. The ids were identified in game_data.ini in DwarfTherapist.
  skill = { 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 }
  --These are the military skills that will get higher values. Subset of the list above.
  military = { 38,39,41,42,43,44,45,46,54,99,100,101,102,103,104,105 }

  --ipairs iterates over each indexed item in the skill list. Lua index is 1 based, so remember that.
  for sk,sv in ipairs(skill) do
    --assume the labor rating will apply
    new_rating = labor_rating
    --iterate over the military list
    for _,v in ipairs(military) do
      --if the value from the skill list iteration is in the military list...
      if v == sv then
        --then use the military skill level
        new_rating = military_rating
      end
    end
    --the skills need to be inserted in a particular way. This standard function passes the id, the skill level, and the experience value, and tells the tool to keep the skills sorted by id. 
    --the experience calculation reflects the minimum experience to earn the level and allows skills to continue to advance.
    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
  
  -- walk over each of the skills we just set and print them out so we can confirm each update
  for sk,sv in ipairs(unit.status.current_soul.skills) do
    -- print the skill data. Printall will print entire tables and is handy at poking at the data structures
    printall(sv)
  end
end

--add the function above to the dfusion menu system as a submenu under the 'Tools' menu.
tools.menu:add("Morul",tools.morul)

Tips and Tricks[edit]

  • To use dfusion's functionality in lua first run dfuse and then use run lua
  • You can bind keys to lua commands (in dfusion context) like this: keybindings add Shift-R "dfuse adv_tools.reincarnate()"