dfhack/plugins/ruby/README

147 lines
5.4 KiB
Plaintext

2012-04-24 17:20:35 -06:00
This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress).
The plugin maps all the structures available in library/xml/ to ruby objects.
2012-06-24 09:30:26 -06:00
These objects are described in ruby-autogen.rb, they are all in the DFHack
module. The toplevel 'df' method is a shortcut to the DFHack module.
2012-04-24 17:20:35 -06:00
The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct
access to the raw DF data structures in memory is provided.
2012-06-24 09:30:26 -06:00
Some library methods are stored in the various .rb file, e.g. shortcuts to read
a map block, find an unit or an item, etc.
2012-04-24 17:20:35 -06:00
2012-06-24 09:30:26 -06:00
Global dfhack objects are accessible through the 'df' accessor (eg 'df.world').
2012-04-24 17:20:35 -06:00
2012-06-24 09:30:26 -06:00
DFHack console
--------------
The ruby plugin defines 1 dfhack console command:
rb_eval <ruby expression> ; evaluate a ruby expression and show the result in
the console. Ex: rb_eval df.unit_find().name.first_name
2012-04-24 17:20:35 -06:00
You can use single-quotes for strings ; avoid double-quotes that are parsed
2012-06-24 09:30:26 -06:00
and removed by the dfhack console code.
Text output from ruby code, through the standard 'puts', 'p' or 'raise' are
redirected to the dfhack console window.
2012-04-24 17:20:35 -06:00
If dfhack reports 'rb_eval is not a recognized command', check stderr.log. You
need a valid 32-bit ruby library to work, and ruby1.8 is prefered (ruby1.9 may
crash DF on startup for now). Install the library in the df root folder (or
2012-06-24 09:30:26 -06:00
df/hack/ on linux), the library should be named 'libruby.dll' (.so on linux).
You can download a tested version at http://github.com/jjyg/dfhack/downloads/
2012-06-24 09:30:26 -06:00
DFHack callbacks
----------------
The plugin interfaces with dfhack 'onupdate' hook.
2012-04-24 17:20:35 -06:00
To register ruby code to be run every graphic frame, use:
handle = df.onupdate_register { puts 'i love flooding the console' }
2012-04-24 17:20:35 -06:00
To stop being called, use:
df.onupdate_unregister handle
2012-06-24 09:30:26 -06:00
The same mechanism is available for 'onstatechange'.
C++ object manipulation
-----------------------
The ruby classes defined in ruby-autogen.rb are accessors to the underlying
df C++ objects in-memory. To allocate a new C++ object for use in DF, use the
RubyClass.cpp_new method (see buildings.rb for exemples), works for Compounds
only.
Deallocation is not supported. You may manually call df.free if you know
what you are doing (maps directly to the native malloc/free)
C++ std::string fields may be directly re-allocated using standard ruby strings,
e.g. some_unit.name.nickname = 'moo'
More subtle string manipulation, e.g. changing a single character, are not
supported. Read the whole string, manipulate it in ruby, and re-assign it
instead.
C++ std::vector<> can be iterated as standard ruby Enumerable objects, using
each/map/etc.
To append data to a vector, use vector << newelement or vector.push(newelement)
To insert at a given pos, vector.insert_at(index, value)
To delete an element, vector.delete_at(index)
You can binary search an element in a vector for a given numeric field value:
df.world.unit.all.binsearch(42, :id)
will find the element whose 'id' field is 42 (needs the vector to be initially
sorted by this field). The binsearch 2nd argument defaults to :id.
Any numeric field defined as being an enum value will be converted to a ruby
Symbol. This works for array indexes too.
Virtual method calls are supported for C++ objects, with a maximum of 4
arguments. Arguments / return value are interpreted as Compound/Enums as
specified in the vmethod definition in the xmls.
Pointer fields are automatically dereferenced ; so a vector of pointer to
Units will yield Units directly. NULL pointers yield the 'nil' value.
2012-04-24 17:20:35 -06:00
Exemples
--------
2012-06-24 09:30:26 -06:00
For more complex exemples, check the dfhack/scripts/*.rb files.
2012-04-24 17:20:35 -06:00
Show info on the currently selected unit ('v' or 'k' DF menu)
2012-06-24 09:30:26 -06:00
p df.unit_find.flags1
2012-04-24 17:20:35 -06:00
Set a custom nickname to unit with id '123'
2012-06-24 09:30:26 -06:00
df.unit_find(123).name.nickname = 'moo'
2012-04-24 17:20:35 -06:00
Show current unit profession
2012-06-24 09:30:26 -06:00
p df.unit_find.profession
2012-04-24 17:20:35 -06:00
Change current unit profession
2012-06-24 09:30:26 -06:00
df.unit_find.profession = :MASON
2012-06-24 09:30:26 -06:00
Center the screen on unit ID '123'
df.center_viewscreen(df.unit_find(123))
2012-04-24 17:20:35 -06:00
Find an item at a given position, show its C++ classname
2012-06-24 09:30:26 -06:00
p df.item_find(df.cursor)._rtti_classname
2012-04-24 17:20:35 -06:00
Find the raws name of the plant under cursor
plant = df.world.plants.all.find { |plt| df.at_cursor?(plt) }
p df.world.raws.plants.all[plant.mat_index].id
2012-04-24 17:20:35 -06:00
Dig a channel under the cursor
df.map_designation_at(df.cursor).dig = :Channel
2012-04-24 17:20:35 -06:00
df.map_block_at(df.cursor).flags.designated = true
2012-06-24 09:30:26 -06:00
Plugin compilation
------------------
2012-04-24 17:20:35 -06:00
2012-06-24 09:30:26 -06:00
The plugin consists of the *.rb file including user comfort functions and
describing basic classes used by the autogenerated code, and ruby-autogen.rb,
the auto-generated code.
2012-04-24 17:20:35 -06:00
2012-06-24 09:30:26 -06:00
autogen is output by codegen.pl from dfhack/library/include/df/codegen.out.xml
2012-04-24 17:20:35 -06:00
For exemple,
2012-04-24 17:20:35 -06:00
<ld:global-type ld:meta="struct-type" type-name="unit">
<ld:field type-name="language_name" name="name" ld:meta="global"/>
<ld:field name="custom_profession" ld:meta="primitive" ld:subtype="stl-string"/>
<ld:field ld:subtype="enum" base-type="int16_t" name="profession" type-name="profession" ld:meta="global"/>
Will generate
2012-04-24 17:20:35 -06:00
class Unit < MemHack::Compound
field(:name, 0) { global :LanguageName }
field(:custom_profession, 60) { stl_string }
field(:profession, 64) { number 16, true }
2012-06-24 09:30:26 -06:00
The syntax for the 'field' method in ruby-autogen.rb is:
1st argument = name of the method
2012-06-24 09:30:26 -06:00
2nd argument = offset of this field from the beginning of the current struct.
The block argument describes the type of the field: uint32, ptr to global...
2012-06-24 09:30:26 -06:00
Primitive type access is done through native methods from ruby.cpp (vector length,
2012-04-24 17:20:35 -06:00
raw memory access, etc)