dfhack/plugins/ruby
jj 3e61452f15 ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
..
plugins ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
CMakeLists.txt ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
README ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
codegen.pl ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
ruby.cpp ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00
ruby.rb ruby: codegen code cleanup, move ruby-memstruct in ruby.rb, handle bool struct fields, rename Enum.to_sym/to_i to sym()/int(), define nested compound sizeof() 2012-05-29 18:30:18 +02:00

README

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.

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.

The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct
access to the raw DF data structures in memory is provided.

Some library methods are stored in the ruby.rb file, with shortcuts to read a
map block, find an unit or an item, etc.

Global objects are stored in the GlobalObjects class ; each object accessor is
mirrored as a DFHack module method (eg df.world).

The ruby plugin defines 2 dfhack console commands:
 rb_load <filename> ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes)
 rb_eval <ruby expression> ; evaluate a ruby expression, show the result in the
console. Ex: rb_eval df.find_unit.name.first_name
You can use single-quotes for strings ; avoid double-quotes that are parsed
and removed by the dfhack console.

The plugin also 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' }
To stop being called, use:
 df.onupdate_unregister handle

The same mechanism is available for onstatechange.


Exemples
--------

For more complex exemples, check the ruby/plugins/ folder.

Show info on the currently selected unit ('v' or 'k' DF menu)
 p df.find_unit.flags1

Set a custom nickname to unit with id '123'
 df.find_unit(123).name.nickname = 'moo'

Show current unit profession
 p df.find_unit.profession

Change current unit profession
 df.find_unit.profession = :MASON

Center the screen on unit '123'
 df.center_viewscreen(df.find_unit(123))

Find an item at a given position, show its C++ classname
 df.find_item(df.cursor)._rtti_classname

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

Dig a channel under the cursor
 df.map_designation_at(df.cursor).dig = :Channel
 df.map_block_at(df.cursor).flags.designated = true


Compilation
-----------

The plugin consists of the ruby.rb file including user comfort functions and
describing basic classes used by the autogenerated code, and ruby-autogen.rb,
the auto-generated code.

The generated code is generated by codegen.pl, which takes the codegen.out.xml
file as input.

For exemple,
 <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
 class Unit < MemHack::Compound
  field(:name, 0) { global :LanguageName }
  field(:custom_profession, 60) { stl_string }
  field(:profession, 64) { number 16, true }

The syntax for the 'field' method is:
1st argument = name of the method
2nd argument = offset of this field from the beginning of the struct.

The block argument describes the type of the field: uint32, ptr to global...

Primitive type access is done through native methods in ruby.cpp (vector length,
raw memory access, etc)

MemHack::Pointers are automatically dereferenced ; so a vector of pointer to
Units will yield Units directly. Null pointers yield the 'nil' value.

This allows to use code such as 'df.world.units.all[0].pos', with 'all' being
in fact a vector of *pointers* to DFHack::Unit objects.