turn down Ruby support
							parent
							
								
									17bcdb1f56
								
							
						
					
					
						commit
						7cf703ef23
					
				| @ -1,31 +0,0 @@ | |||||||
| .. _rb: |  | ||||||
| 
 |  | ||||||
| ruby |  | ||||||
| ==== |  | ||||||
| 
 |  | ||||||
| .. dfhack-tool:: |  | ||||||
|     :summary: Allow Ruby scripts to be executed as DFHack commands. |  | ||||||
|     :tags: dev |  | ||||||
|     :no-command: |  | ||||||
| 
 |  | ||||||
| .. dfhack-command:: rb |  | ||||||
|    :summary: Eval() a ruby string. |  | ||||||
| 
 |  | ||||||
| .. dfhack-command:: rb_eval |  | ||||||
|    :summary: Eval() a ruby string. |  | ||||||
| 
 |  | ||||||
| Usage |  | ||||||
| ----- |  | ||||||
| 
 |  | ||||||
| :: |  | ||||||
| 
 |  | ||||||
|     enable ruby |  | ||||||
|     rb "ruby expression" |  | ||||||
|     rb_eval "ruby expression" |  | ||||||
|     :rb ruby expression |  | ||||||
| 
 |  | ||||||
| Example |  | ||||||
| ------- |  | ||||||
| 
 |  | ||||||
| ``:rb puts df.unit_find(:selected).name`` |  | ||||||
|     Print the name of the selected unit. |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| # Allow build system to turn off downloading of libruby.so. |  | ||||||
| option(DOWNLOAD_RUBY "Download prebuilt libruby.so for ruby plugin." ON) |  | ||||||
| 
 |  | ||||||
| if(DOWNLOAD_RUBY) |  | ||||||
| 
 |  | ||||||
|     if(APPLE) |  | ||||||
|         set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/osx${DFHACK_BUILD_ARCH}/libruby.dylib) |  | ||||||
|         set(RUBYLIB_INSTALL_NAME "libruby.dylib") |  | ||||||
|         if(${DFHACK_BUILD_ARCH} STREQUAL 64) |  | ||||||
|             # message("No ruby lib for 64-bit OS X yet") |  | ||||||
|         else() |  | ||||||
|             download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/osx32-libruby187.dylib.gz" |  | ||||||
|                 "gz" |  | ||||||
|                 ${RUBYLIB}.gz |  | ||||||
|                 "e9bc4263557e652121b055a46abb4f97" |  | ||||||
|                 ${RUBYLIB} |  | ||||||
|                 "3ee5356759f764a440be5b5b44649826") |  | ||||||
|         endif() |  | ||||||
|     elseif(UNIX) |  | ||||||
|         set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/linux${DFHACK_BUILD_ARCH}/libruby.so) |  | ||||||
|         set(RUBYLIB_INSTALL_NAME "libruby.so") |  | ||||||
|         if(${DFHACK_BUILD_ARCH} STREQUAL 64) |  | ||||||
|             download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/linux64-libruby187.so.gz" |  | ||||||
|                 "gz" |  | ||||||
|                 ${RUBYLIB}.gz |  | ||||||
|                 "8eb757bb9ada08608914d8ca8906c427" |  | ||||||
|                 ${RUBYLIB} |  | ||||||
|                 "e8c36a06f031cfbf02def28169bb5f1f") |  | ||||||
|         else() |  | ||||||
|             download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/linux32-libruby187.so.gz" |  | ||||||
|                 "gz" |  | ||||||
|                 ${RUBYLIB}.gz |  | ||||||
|                 "2d06f5069ff07ea934ecd40db55a4ac5" |  | ||||||
|                 ${RUBYLIB} |  | ||||||
|                 "b00d8d7086cb39f6fde793f9d89cb2d7") |  | ||||||
|         endif() |  | ||||||
|     else() |  | ||||||
|         set(RUBYLIB ${CMAKE_CURRENT_SOURCE_DIR}/win${DFHACK_BUILD_ARCH}/libruby.dll) |  | ||||||
|         set(RUBYLIB_INSTALL_NAME "libruby.dll") |  | ||||||
|         if(${DFHACK_BUILD_ARCH} STREQUAL 64) |  | ||||||
|             download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win64-libruby200.dll.gz" |  | ||||||
|                 "gz" |  | ||||||
|                 ${RUBYLIB}.gz |  | ||||||
|                 "81db54a8b8b3090c94c6ae2147d30b8f" |  | ||||||
|                 ${RUBYLIB} |  | ||||||
|                 "8a8564418aebddef3dfee1e96690e713") |  | ||||||
|         else() |  | ||||||
|             download_file_unzip("https://github.com/DFHack/dfhack-bin/releases/download/0.44.09/win32-libruby187.dll.gz" |  | ||||||
|                 "gz" |  | ||||||
|                 ${RUBYLIB}.gz |  | ||||||
|                 "ffc0f1b5b33748e2a36128e90c97f6b2" |  | ||||||
|                 ${RUBYLIB} |  | ||||||
|                 "482c1c418f4ee1a5f04203eee1cda0ef") |  | ||||||
|         endif() |  | ||||||
|     endif() |  | ||||||
| 
 |  | ||||||
| endif() |  | ||||||
| 
 |  | ||||||
| if(APPLE OR UNIX) |  | ||||||
|     set(RUBYAUTOGEN ruby-autogen-gcc.rb) |  | ||||||
| else(APPLE OR UNIX) |  | ||||||
|     set(RUBYAUTOGEN ruby-autogen-win.rb) |  | ||||||
| endif(APPLE OR UNIX) |  | ||||||
| 
 |  | ||||||
| add_custom_command( |  | ||||||
|     OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} |  | ||||||
|     COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} ${CMAKE_SYSTEM_NAME} ${DFHACK_BUILD_ARCH} |  | ||||||
|     # cmake quirk: depending on codegen.out.xml or generate_headers only is not enough, needs both |  | ||||||
|     # test by manually patching any library/xml/moo.xml, run make ruby-autogen-rb -j2, and check build/plugins/ruby/ruby-autogen.rb for patched xml data |  | ||||||
|     DEPENDS generate_headers ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl |  | ||||||
|     COMMENT ${RUBYAUTOGEN} |  | ||||||
| ) |  | ||||||
| add_custom_target(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN}) |  | ||||||
| 
 |  | ||||||
| include_directories("${dfhack_SOURCE_DIR}/depends/tthread") |  | ||||||
| 
 |  | ||||||
| dfhack_plugin(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) |  | ||||||
| add_dependencies(ruby ruby-autogen-rb) |  | ||||||
| 
 |  | ||||||
| if(EXISTS ${RUBYLIB}) |  | ||||||
|     install(FILES ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION} RENAME ${RUBYLIB_INSTALL_NAME}) |  | ||||||
| else() |  | ||||||
|     # Only fire this warning if DOWNLOAD_RUBY was set. |  | ||||||
|     if(NOT(APPLE AND ${DFHACK_BUILD_ARCH} STREQUAL 64) AND DOWNLOAD_RUBY) |  | ||||||
|         message(WARNING "Ruby library not found at ${RUBYLIB} - will not be installed") |  | ||||||
|     endif() |  | ||||||
| endif() |  | ||||||
| 
 |  | ||||||
| install(DIRECTORY . |  | ||||||
|     DESTINATION hack/ruby |  | ||||||
|     FILES_MATCHING PATTERN "*.rb") |  | ||||||
| 
 |  | ||||||
| install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${RUBYAUTOGEN} DESTINATION hack/ruby) |  | ||||||
| @ -1,279 +0,0 @@ | |||||||
| 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 various .rb file, e.g. shortcuts to read |  | ||||||
| a map block, find an unit or an item, etc. |  | ||||||
| 
 |  | ||||||
| Global dfhack objects are accessible through the 'df' accessor (eg 'df.world'). |  | ||||||
| 
 |  | ||||||
| DFHack structures are renamed in CamelCase in the ruby namespace. |  | ||||||
| 
 |  | ||||||
| For a list of the structures and their methods, grep the ruby-autogen.rb file. |  | ||||||
| 
 |  | ||||||
| All ruby code runs while the main DF process and other plugins are suspended. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| DFHack console |  | ||||||
| -------------- |  | ||||||
| 
 |  | ||||||
| The ruby plugin defines one new 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 |  | ||||||
| You can use single-quotes for strings ; avoid double-quotes that are parsed |  | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| 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 |  | ||||||
| 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/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Ruby scripts |  | ||||||
| ------------ |  | ||||||
| 
 |  | ||||||
| The ruby plugin allows the creation of '.rb' scripts in df/hack/scripts/. |  | ||||||
| 
 |  | ||||||
| If you create such a script, e.g. 'test.rb', that will add a new dfhack console |  | ||||||
| command 'test'. |  | ||||||
| The script can access the console command arguments through the global variable |  | ||||||
| '$script_args', which is an array of ruby Strings. |  | ||||||
| To exit early from a script, use 'throw :script_finished' |  | ||||||
| 
 |  | ||||||
| The help string displayed in dfhack 'ls' command is the first line of the |  | ||||||
| script, if it is a comment (ie starts with '# '). |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Calling dfhack commands |  | ||||||
| ----------------------- |  | ||||||
| 
 |  | ||||||
| The ruby plugin allows the calling of arbitrary dfhack commands, as if typed |  | ||||||
| directly on the dfhack prompt. |  | ||||||
| However due to locks and stuff, the dfhack command is delayed until the current |  | ||||||
| ruby command is finished, so it is restricted to interactive uses. |  | ||||||
| It is possible to call the method many times, this will queue dfhack commands |  | ||||||
| to be run in order. |  | ||||||
| 
 |  | ||||||
|  df.dfhack_run "reveal" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Ruby helper functions |  | ||||||
| --------------------- |  | ||||||
| 
 |  | ||||||
| This is an excerpt of the functions defined in dfhack/plugins/ruby/*.rb. Check |  | ||||||
| the files and the comments for a complete list. |  | ||||||
| 
 |  | ||||||
|  df.same_pos?(obj1, obj2) |  | ||||||
| Returns true if both objects are at the same game coordinates. |  | ||||||
| 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, holding all informations wrt the map tile (read&write). |  | ||||||
| This class is a ruby specific extention, to facilitate interaction with the |  | ||||||
| DF map data. Check out hack/ruby/map.rb. |  | ||||||
| 
 |  | ||||||
|  df.each_map_block { |b|  } |  | ||||||
|  df.each_map_block_z(zlevel) { |b|  } |  | ||||||
| Iterates over every map block (opt. on a single z-level). |  | ||||||
| 
 |  | ||||||
|  df.center_viewscreen(coords) |  | ||||||
| Centers the DF view on the given coordinates. Accepts x/y/z arguments, or a |  | ||||||
| single argument responding to pos/x/y/z, eg an Unit, Item, ... |  | ||||||
| 
 |  | ||||||
|  df.unit_find(arg) |  | ||||||
| Returns an Unit. |  | ||||||
| With no arg, returns the currently selected unit (through the (v) or (k) menus) |  | ||||||
| With a number, returns the unit with this ID |  | ||||||
| With something else, returns the first unit at the same game coordinates |  | ||||||
| 
 |  | ||||||
|  df.unit_workers |  | ||||||
| Returns a list of worker citizen: units of your race & civilization, adults, |  | ||||||
| not dead, crazy, ghosts or nobles exempted of work. |  | ||||||
| 
 |  | ||||||
|  df.unit_entitypositions(unit) |  | ||||||
| Returns the list of EntityPosition occupied by the unit. |  | ||||||
| Check the 'code' field for a readable name (MANAGER, CHIEF_MEDICAL_DWARF, ...) |  | ||||||
| 
 |  | ||||||
|  df.match_rawname(name, list) |  | ||||||
| String fuzzy matching. Returns the list entry most similar to 'name'. |  | ||||||
| First searches for an exact match, then for a case-insensitive match, and |  | ||||||
| finally for a case-insensitive substring. |  | ||||||
| Returns the element from list if there is only one match, or nil. |  | ||||||
| Most useful to allow the user to specify a raw-defined name, |  | ||||||
| eg 'gob' for 'GOBLIN' or 'coal' for 'COAL_BITUMINOUS', hence the name. |  | ||||||
| 
 |  | ||||||
|  df.building_alloc(type, subtype, customtype) |  | ||||||
|  df.building_position(bld, pos, w, h) |  | ||||||
|  df.building_construct(bld, item_list) |  | ||||||
| Allocates a new building in DF memory, define its position / dimensions, and |  | ||||||
| create a dwarf job to construct it from the given list of items. |  | ||||||
| See buildings.rb/buildbed for an example. |  | ||||||
| 
 |  | ||||||
|  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 |  | ||||||
| ---------------- |  | ||||||
| 
 |  | ||||||
| The plugin interfaces with dfhack 'onupdate' hook. |  | ||||||
| To register ruby code to be run every graphic frame, use: |  | ||||||
|  handle = df.onupdate_register('log') { 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('myname', 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 |  | ||||||
| 
 |  | ||||||
| The same mechanism is available for 'onstatechange', but the |  | ||||||
| SC_BEGIN_UNLOAD event is not propagated to the ruby handler. |  | ||||||
| 
 |  | ||||||
| Available states: |  | ||||||
|  :WORLD_LOADED, :WORLD_UNLOADED, :MAP_LOADED, :MAP_UNLOADED, |  | ||||||
|  :VIEWSCREEN_CHANGED, :CORE_INITIALIZED, :PAUSED, :UNPAUSED |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 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 examples), works for Compounds |  | ||||||
| only. |  | ||||||
| A special Compound DFHack::StlString is available for allocating a single c++ |  | ||||||
| stl::string, so that you can call vmethods that take a string pointer argument |  | ||||||
| (eg getName). |  | ||||||
|  ex: s = DFHack::StlString.cpp_new ; df.building_find.getName(s) ; p s.str |  | ||||||
| 
 |  | ||||||
| Deallocation may work, using the compound method _cpp_delete. Use with caution, |  | ||||||
| may crash your DF session. It may be simpler to just leak the memory. |  | ||||||
| _cpp_delete will try to free all memory directly used by the compound, eg |  | ||||||
| strings and vectors. It will *not* call the class destructor, and will not free |  | ||||||
| stuff behind pointers. |  | ||||||
| 
 |  | ||||||
| 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 entry 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. |  | ||||||
|  ex: df.unit_find(:selected).status.labors[:HAUL_FOOD] = true |  | ||||||
|      df.map_tile_at(df.cursor).designation.liquid_type = :Water |  | ||||||
| 
 |  | ||||||
| Virtual method calls are supported for C++ objects, with a maximum of 6 |  | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Examples |  | ||||||
| -------- |  | ||||||
| 
 |  | ||||||
| For more complex examples, check the dfhack/scripts/*.rb files. |  | ||||||
| 
 |  | ||||||
| Show info on the currently selected unit ('v' or 'k' DF menu) |  | ||||||
|  p df.unit_find.flags1 |  | ||||||
| 
 |  | ||||||
| Set a custom nickname to unit with id '123' |  | ||||||
|  df.unit_find(123).name.nickname = 'moo' |  | ||||||
| 
 |  | ||||||
| Show current unit profession |  | ||||||
|  p df.unit_find.profession |  | ||||||
| 
 |  | ||||||
| Change current unit profession |  | ||||||
|  df.unit_find.profession = :MASON |  | ||||||
| 
 |  | ||||||
| Center the screen on unit ID '123' |  | ||||||
|  df.center_viewscreen(df.unit_find(123)) |  | ||||||
| 
 |  | ||||||
| Find an item under the game cursor and show its C++ classname |  | ||||||
|  p df.item_find(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_tile_at(df.cursor).dig(:Channel) |  | ||||||
| 
 |  | ||||||
| Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet' |  | ||||||
|  hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' } |  | ||||||
|  df.map_tile_at(hot).spawn_magma(2) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Plugin compilation |  | ||||||
| ------------------ |  | ||||||
| 
 |  | ||||||
| The plugin consists of the main ruby.cpp native plugin and the *.rb files. |  | ||||||
| 
 |  | ||||||
| The native plugin handles only low-level ruby-to-df interaction (eg raw memory |  | ||||||
| read/write, and dfhack integration), and the .rb files hold end-user helper |  | ||||||
| functions. |  | ||||||
| 
 |  | ||||||
| On dfhack start, the native plugin will initialize the ruby interpreter, and |  | ||||||
| load hack/ruby/ruby.rb. This one then loads all other .rb files. |  | ||||||
| 
 |  | ||||||
| The DF internal structures are described in ruby-autogen.rb . |  | ||||||
| It is output by ruby/codegen.pl, from dfhack/library/include/df/codegen.out.xml |  | ||||||
| It contains architecture-specific data (eg DF internal structures field offsets, |  | ||||||
| which differ between Windows and Linux. Linux and Macosx are the same, as they |  | ||||||
| both use gcc). |  | ||||||
| It is stored inside the build directory (eg build/plugins/ruby/ruby-autogen.rb) |  | ||||||
| 
 |  | ||||||
| For example, |  | ||||||
|  <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 in ruby-autogen.rb is: |  | ||||||
| 1st argument = name of the method |  | ||||||
| 2nd argument = offset of this field from the beginning of the current struct. |  | ||||||
|  This field depends on the compiler used by Toady to generate DF. |  | ||||||
| The block argument describes the type of the field: uint32, ptr to global... |  | ||||||
| 
 |  | ||||||
| Primitive type access is done through native methods from ruby.cpp (vector length, |  | ||||||
| raw memory access, etc) |  | ||||||
| @ -1,368 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         def building_find(what=:selected, y=nil, z=nil) |  | ||||||
|             if what == :selected |  | ||||||
|                 return world.buildings.all.binsearch(df.get_selected_building_id) |  | ||||||
|             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] |  | ||||||
|             raise "invalid building type #{type.inspect}" if not cls |  | ||||||
|             bld = cls.cpp_new |  | ||||||
|             bld.race = ui.race_id |  | ||||||
|             subtype = ConstructionType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Construction |  | ||||||
|             subtype = SiegeengineType.int(subtype) if subtype.kind_of?(::Symbol) and type == :SiegeEngine |  | ||||||
|             subtype = WorkshopType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Workshop |  | ||||||
|             subtype = FurnaceType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Furnace |  | ||||||
|             subtype = CivzoneType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Civzone |  | ||||||
|             subtype = TrapType.int(subtype) if subtype.kind_of?(::Symbol) and type == :Trap |  | ||||||
|             bld.setSubtype(subtype) |  | ||||||
|             bld.setCustomType(custom) |  | ||||||
|             case type |  | ||||||
|             when :Well; bld.bucket_z = bld.z |  | ||||||
|             when :Furnace; bld.melt_remainder[world.raws.inorganics.length] = 0 |  | ||||||
|             when :Coffin; bld.initBurialFlags |  | ||||||
|             when :Trap; bld.ready_timeout = 500 if bld.trap_type == :PressurePlate |  | ||||||
|             when :Floodgate; bld.gate_flags.closed = true |  | ||||||
|             when :GrateWall; bld.gate_flags.closed = true |  | ||||||
|             when :GrateFloor; bld.gate_flags.closed = true |  | ||||||
|             when :BarsVertical; bld.gate_flags.closed = true |  | ||||||
|             when :BarsFloor; bld.gate_flags.closed = true |  | ||||||
|             end |  | ||||||
|             bld |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # used by building_setsize |  | ||||||
|         def building_check_bridge_support(bld) |  | ||||||
|             x1 = bld.x1-1 |  | ||||||
|             x2 = bld.x2+1 |  | ||||||
|             y1 = bld.y1-1 |  | ||||||
|             y2 = bld.y2+1 |  | ||||||
|             z = bld.z |  | ||||||
|             (x1..x2).each { |x| |  | ||||||
|                 (y1..y2).each { |y| |  | ||||||
|                     next if ((x == x1 or x == x2) and |  | ||||||
|                              (y == y1 or y == y2)) |  | ||||||
|                     if mb = map_block_at(x, y, z) and tile = mb.tiletype[x%16][y%16] and TiletypeShape::BasicShape[Tiletype::Shape[tile]] != :Open |  | ||||||
|                         bld.gate_flags.has_support = true |  | ||||||
|                         return |  | ||||||
|                     end |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             bld.gate_flags.has_support = false |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # sets x2/centerx/y2/centery from x1/y1/bldtype |  | ||||||
|         # x2/y2 preserved for :FarmPlot etc |  | ||||||
|         def building_setsize(bld) |  | ||||||
|             bld.x2 = bld.x1 if bld.x1 > bld.x2 |  | ||||||
|             bld.y2 = bld.y1 if bld.y1 > bld.y2 |  | ||||||
|             case bld.getType |  | ||||||
|             when :Bridge |  | ||||||
|                 bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 |  | ||||||
|                 bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 |  | ||||||
|                 building_check_bridge_support(bld) |  | ||||||
|             when :FarmPlot, :RoadDirt, :RoadPaved, :Stockpile, :Civzone |  | ||||||
|                 bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 |  | ||||||
|                 bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 |  | ||||||
|             when :TradeDepot, :Shop |  | ||||||
|                 bld.x2 = bld.x1+4 |  | ||||||
|                 bld.y2 = bld.y1+4 |  | ||||||
|                 bld.centerx = bld.x1+2 |  | ||||||
|                 bld.centery = bld.y1+2 |  | ||||||
|             when :SiegeEngine, :Windmill, :Wagon |  | ||||||
|                 bld.x2 = bld.x1+2 |  | ||||||
|                 bld.y2 = bld.y1+2 |  | ||||||
|                 bld.centerx = bld.x1+1 |  | ||||||
|                 bld.centery = bld.y1+1 |  | ||||||
|             when :AxleHorizontal |  | ||||||
|                 if bld.is_vertical == 1 |  | ||||||
|                     bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                     bld.centery = bld.y1 + (bld.y2+1-bld.y1)/2 |  | ||||||
|                 else |  | ||||||
|                     bld.centerx = bld.x1 + (bld.x2+1-bld.x1)/2 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                 end |  | ||||||
|             when :WaterWheel |  | ||||||
|                 if bld.is_vertical == 1 |  | ||||||
|                     bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                     bld.y2 = bld.y1+2 |  | ||||||
|                     bld.centery = bld.y1+1 |  | ||||||
|                 else |  | ||||||
|                     bld.x2 = bld.x1+2 |  | ||||||
|                     bld.centerx = bld.x1+1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                 end |  | ||||||
|             when :Workshop, :Furnace |  | ||||||
|                 # Furnace = Custom or default case only |  | ||||||
|                 case bld.type |  | ||||||
|                 when :Quern, :Millstone, :Tool |  | ||||||
|                     bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                 when :Siege, :Kennels |  | ||||||
|                     bld.x2 = bld.x1+4 |  | ||||||
|                     bld.y2 = bld.y1+4 |  | ||||||
|                     bld.centerx = bld.x1+2 |  | ||||||
|                     bld.centery = bld.y1+2 |  | ||||||
|                 when :Custom |  | ||||||
|                     if bdef = world.raws.buildings.all.binsearch(bld.getCustomType) |  | ||||||
|                         bld.x2 = bld.x1 + bdef.dim_x - 1 |  | ||||||
|                         bld.y2 = bld.y1 + bdef.dim_y - 1 |  | ||||||
|                         bld.centerx = bld.x1 + bdef.workloc_x |  | ||||||
|                         bld.centery = bld.y1 + bdef.workloc_y |  | ||||||
|                     end |  | ||||||
|                 else |  | ||||||
|                     bld.x2 = bld.x1+2 |  | ||||||
|                     bld.y2 = bld.y1+2 |  | ||||||
|                     bld.centerx = bld.x1+1 |  | ||||||
|                     bld.centery = bld.y1+1 |  | ||||||
|                 end |  | ||||||
|             when :ScrewPump |  | ||||||
|                 case bld.direction |  | ||||||
|                 when :FromEast |  | ||||||
|                     bld.x2 = bld.centerx = bld.x1+1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                 when :FromSouth |  | ||||||
|                     bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1+1 |  | ||||||
|                 when :FromWest |  | ||||||
|                     bld.x2 = bld.x1+1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                     bld.centerx = bld.x1 |  | ||||||
|                 else |  | ||||||
|                     bld.x2 = bld.x1+1 |  | ||||||
|                     bld.y2 = bld.centery = bld.y1 |  | ||||||
|                     bld.centerx = bld.x1 |  | ||||||
|                 end |  | ||||||
|             when :Well |  | ||||||
|                 bld.bucket_z = bld.z |  | ||||||
|                 bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                 bld.y2 = bld.centery = bld.y1 |  | ||||||
|             when :Construction |  | ||||||
|                 bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                 bld.y2 = bld.centery = bld.y1 |  | ||||||
|                 bld.setMaterialAmount(1) |  | ||||||
|                 return |  | ||||||
|             else |  | ||||||
|                 bld.x2 = bld.centerx = bld.x1 |  | ||||||
|                 bld.y2 = bld.centery = bld.y1 |  | ||||||
|             end |  | ||||||
|             bld.setMaterialAmount((bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # set building at position, with optional width/height |  | ||||||
|         def building_position(bld, pos, w=nil, h=nil) |  | ||||||
|             if pos.respond_to?(:x1) |  | ||||||
|                 x, y, z = pos.x1, pos.y1, pos.z |  | ||||||
|                 w ||= pos.x2-pos.x1+1 if pos.respond_to?(:x2) |  | ||||||
|                 h ||= pos.y2-pos.y1+1 if pos.respond_to?(:y2) |  | ||||||
|             elsif pos.respond_to?(:x) |  | ||||||
|                 x, y, z = pos.x, pos.y, pos.z |  | ||||||
|             else |  | ||||||
|                 x, y, z = pos |  | ||||||
|             end |  | ||||||
|             w ||= pos.w if pos.respond_to?(:w) |  | ||||||
|             h ||= pos.h if pos.respond_to?(:h) |  | ||||||
|             bld.x1 = x |  | ||||||
|             bld.y1 = y |  | ||||||
|             bld.z  = z |  | ||||||
|             bld.x2 = bld.x1+w-1 if w |  | ||||||
|             bld.y2 = bld.y1+h-1 if h |  | ||||||
|             building_setsize(bld) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # set map occupancy/stockpile/etc for a building |  | ||||||
|         def building_setoccupancy(bld) |  | ||||||
|             stockpile = (bld.getType == :Stockpile) |  | ||||||
|             complete = (bld.getBuildStage >= bld.getMaxBuildStage) |  | ||||||
|             extents = (bld.room.extents and bld.isExtentShaped) |  | ||||||
| 
 |  | ||||||
|             z = bld.z |  | ||||||
|             (bld.x1..bld.x2).each { |x| |  | ||||||
|                 (bld.y1..bld.y2).each { |y| |  | ||||||
|                     next if extents and bld.room.extents[bld.room.width*(y-bld.room.y)+(x-bld.room.x)] == 0 |  | ||||||
|                     next if not mb = map_block_at(x, y, z) |  | ||||||
|                     des = mb.designation[x%16][y%16] |  | ||||||
|                     des.pile = stockpile |  | ||||||
|                     des.dig = :No |  | ||||||
|                     if complete |  | ||||||
|                         bld.updateOccupancy(x, y) |  | ||||||
|                     else |  | ||||||
|                         mb.occupancy[x%16][y%16].building = :Planned |  | ||||||
|                     end |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # link bld into other rooms if it is inside their extents or vice versa |  | ||||||
|         def building_linkrooms(bld) |  | ||||||
|             world.buildings.other[:IN_PLAY].each { |ob| |  | ||||||
|                 next if ob.z != bld.z |  | ||||||
|                 if bld.is_room and bld.room.extents |  | ||||||
|                     next if ob.is_room or ob.x1 < bld.room.x or ob.x1 >= bld.room.x+bld.room.width or ob.y1 < bld.room.y or ob.y1 >= bld.room.y+bld.room.height |  | ||||||
|                     next if bld.room.extents[bld.room.width*(ob.y1-bld.room.y)+(ob.x1-bld.room.x)] == 0 |  | ||||||
|                     ui.equipment.update.buildings = true |  | ||||||
|                     bld.children << ob |  | ||||||
|                     ob.parents << bld |  | ||||||
|                 elsif ob.is_room and ob.room.extents |  | ||||||
|                     next if bld.is_room or bld.x1 < ob.room.x or bld.x1 >= ob.room.x+ob.room.width or bld.y1 < ob.room.y or bld.y1 >= ob.room.y+ob.room.height |  | ||||||
|                     next if ob.room.extents[ob.room.width*(bld.y1-ob.room.y)+(bld.x1-ob.room.x)].to_i == 0 |  | ||||||
|                     ui.equipment.update.buildings = true |  | ||||||
|                     ob.children << bld |  | ||||||
|                     bld.parents << ob |  | ||||||
|                 end |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # link the building into the world, set map data, link rooms, bld.id |  | ||||||
|         def building_link(bld) |  | ||||||
|             bld.id = df.building_next_id |  | ||||||
|             df.building_next_id += 1 |  | ||||||
| 
 |  | ||||||
|             world.buildings.all << bld |  | ||||||
|             bld.categorize(true) |  | ||||||
|             building_setoccupancy(bld) if bld.isSettingOccupancy |  | ||||||
|             building_linkrooms(bld) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # set a design for the building |  | ||||||
|         def building_createdesign(bld, rough=true) |  | ||||||
|             job = bld.jobs[0] |  | ||||||
|             job.mat_type = bld.mat_type |  | ||||||
|             job.mat_index = bld.mat_index |  | ||||||
|             if bld.needsDesign |  | ||||||
|                 bld.design = BuildingDesign.cpp_new |  | ||||||
|                 bld.design.flags.rough = rough |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # creates a job to build bld, return it |  | ||||||
|         def building_linkforconstruct(bld) |  | ||||||
|             building_link bld |  | ||||||
|             ref = GeneralRefBuildingHolderst.cpp_new |  | ||||||
|             ref.building_id = bld.id |  | ||||||
|             job = Job.cpp_new |  | ||||||
|             job.job_type = :ConstructBuilding |  | ||||||
|             job.pos = [bld.centerx, bld.centery, bld.z] |  | ||||||
|             job.general_refs << ref |  | ||||||
|             bld.jobs << job |  | ||||||
|             job_link job |  | ||||||
|             job |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # construct a building with items or JobItems |  | ||||||
|         def building_construct(bld, items) |  | ||||||
|             job = building_linkforconstruct(bld) |  | ||||||
|             rough = false |  | ||||||
|             items.each { |item| |  | ||||||
|                 if items.kind_of?(JobItem) |  | ||||||
|                     item.quantity = (bld.x2-bld.x1+1)*(bld.y2-bld.y1+1)/4+1 if item.quantity < 0 |  | ||||||
|                     job.job_items << item |  | ||||||
|                 else |  | ||||||
|                     job_attachitem(job, item, :Hauled) |  | ||||||
|                 end |  | ||||||
|                 rough = true if item.getType == :BOULDER |  | ||||||
|                 bld.mat_type = item.getMaterial if bld.mat_type == -1 |  | ||||||
|                 bld.mat_index = item.getMaterialIndex if bld.mat_index == -1 |  | ||||||
|             } |  | ||||||
|             building_createdesign(bld, rough) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # construct an abstract building (stockpile, farmplot, ...) |  | ||||||
|         def building_construct_abstract(bld) |  | ||||||
|             case bld.getType |  | ||||||
|             when :Stockpile |  | ||||||
|                 max = df.world.buildings.other[:STOCKPILE].map { |s| s.stockpile_number }.max |  | ||||||
|                 bld.stockpile_number = max.to_i + 1 |  | ||||||
|             when :Civzone |  | ||||||
|                 max = df.world.buildings.other[:ANY_ZONE].map { |z| z.zone_num }.max |  | ||||||
|                 bld.zone_num = max.to_i + 1 |  | ||||||
|             end |  | ||||||
|             building_link bld |  | ||||||
|             if !bld.flags.exists |  | ||||||
|                 bld.flags.exists = true |  | ||||||
|                 bld.initFarmSeasons |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def building_setowner(bld, unit) |  | ||||||
|             return unless bld.is_room |  | ||||||
|             return if bld.owner == unit |  | ||||||
| 
 |  | ||||||
|             if bld.owner |  | ||||||
|                 if idx = bld.owner.owned_buildings.index { |ob| ob.id == bld.id } |  | ||||||
|                     bld.owner.owned_buildings.delete_at(idx) |  | ||||||
|                 end |  | ||||||
|                 if spouse = bld.owner.relations.spouse_tg and |  | ||||||
|                         idx = spouse.owned_buildings.index { |ob| ob.id == bld.id } |  | ||||||
|                     spouse.owned_buildings.delete_at(idx) |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|             bld.owner = unit |  | ||||||
|             if unit |  | ||||||
|                 unit.owned_buildings << bld |  | ||||||
|                 if spouse = bld.owner.relations.spouse_tg and |  | ||||||
|                         !spouse.owned_buildings.index { |ob| ob.id == bld.id } and |  | ||||||
|                         bld.canUseSpouseRoom |  | ||||||
|                     spouse.owned_buildings << bld |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # creates a job to deconstruct the building |  | ||||||
|         def building_deconstruct(bld) |  | ||||||
|             job = Job.cpp_new |  | ||||||
|             refbuildingholder = GeneralRefBuildingHolderst.cpp_new |  | ||||||
|             job.job_type = :DestroyBuilding |  | ||||||
|             refbuildingholder.building_id = bld.id |  | ||||||
|             job.general_refs << refbuildingholder |  | ||||||
|             bld.jobs << job |  | ||||||
|             job_link job |  | ||||||
|             job |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # exemple usage |  | ||||||
|         def buildbed(pos=cursor) |  | ||||||
|             raise 'where to ?' if pos.x < 0 |  | ||||||
| 
 |  | ||||||
|             item = world.items.all.find { |i| |  | ||||||
|                 i.kind_of?(ItemBedst) and |  | ||||||
|                 item_isfree(i) |  | ||||||
|             } |  | ||||||
|             raise 'no free bed, build more !' if not item |  | ||||||
| 
 |  | ||||||
|             bld = building_alloc(:Bed) |  | ||||||
|             building_position(bld, pos) |  | ||||||
|             building_construct(bld, [item]) |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -1,44 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         # return an Item |  | ||||||
|         # arg similar to unit.rb/unit_find; no arg = 'k' menu |  | ||||||
|         def item_find(what=:selected, y=nil, z=nil) |  | ||||||
|             if what == :selected |  | ||||||
|                 return world.items.all.binsearch(df.get_selected_item_id) |  | ||||||
|             elsif what.kind_of?(Integer) |  | ||||||
|                 # 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 |  | ||||||
|                 raise "what what?" |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # check item flags to see if it is suitable for use as a job input material |  | ||||||
|         def item_isfree(i, check_empty=true) |  | ||||||
|             !i.flags.trader and |  | ||||||
|             !i.flags.in_job and |  | ||||||
|             !i.flags.construction and |  | ||||||
|             !i.flags.removed and |  | ||||||
|             !i.flags.forbid and |  | ||||||
|             !i.flags.dump and |  | ||||||
|             !i.flags.owned and |  | ||||||
|             !i.flags.in_chest and   # used as hospital supply ? |  | ||||||
|             (!i.flags.container or not check_empty or |  | ||||||
|                 !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainsItemst) }) and |  | ||||||
|             (!i.flags.in_inventory or |  | ||||||
|                 (!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefUnitHolderst) and   # allow hauled items TODO check if holder is a thief |  | ||||||
|                     ir.unit_tg.inventory.find { |ii| ii.item == i and ii.mode != :Hauled } } and |  | ||||||
|                 !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainedInItemst) and |  | ||||||
|                     !item_isfree(ir.item_tg, false) })) and |  | ||||||
|             (!i.flags.in_building or |  | ||||||
|                 !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefBuildingHolderst) and |  | ||||||
|                     ir.building_tg.contained_items.find { |bi| bi.use_mode == 2 and bi.item == i } }) and |  | ||||||
|             (!i.flags.on_ground or !df.map_tile_at(i).designation.hidden)      # i.flags.unk11? |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         # link a job to the world |  | ||||||
|         # allocate & set job.id, allocate a JobListLink, link to job & world.jobs.list |  | ||||||
|         def job_link(job) |  | ||||||
|             lastjob = world.jobs.list |  | ||||||
|             lastjob = lastjob.next while lastjob.next |  | ||||||
|             joblink = JobListLink.cpp_new |  | ||||||
|             joblink.prev = lastjob |  | ||||||
|             joblink.item = job |  | ||||||
|             job.list_link = joblink |  | ||||||
|             job.id = df.job_next_id |  | ||||||
|             df.job_next_id += 1 |  | ||||||
|             lastjob.next = joblink |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # attach an item to a job, flag item in_job |  | ||||||
|         def job_attachitem(job, item, role=:Hauled, filter_idx=-1) |  | ||||||
|             if role != :TargetContainer |  | ||||||
|                 item.flags.in_job = true |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             itemlink = SpecificRef.cpp_new |  | ||||||
|             itemlink.type = :JOB |  | ||||||
|             itemlink.job = job |  | ||||||
|             item.specific_refs << itemlink |  | ||||||
| 
 |  | ||||||
|             joblink = JobItemRef.cpp_new |  | ||||||
|             joblink.item = item |  | ||||||
|             joblink.role = role |  | ||||||
|             joblink.job_item_idx = filter_idx |  | ||||||
|             job.items << joblink |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
| @ -1,344 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         # return a map block by tile coordinates |  | ||||||
|         # you can also use find_map_block(cursor) or anything that respond to x/y/z |  | ||||||
|         def map_block_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) |  | ||||||
|             if x >= 0 and x < world.map.x_count and y >= 0 and y < world.map.y_count and z >= 0 and z < world.map.z_count |  | ||||||
|                 world.map.block_index[x/16][y/16][z] |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def map_designation_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) |  | ||||||
|             if b = map_block_at(x, y, z) |  | ||||||
|                 b.designation[x%16][y%16] |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def map_occupancy_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) |  | ||||||
|             if b = map_block_at(x, y, z) |  | ||||||
|                 b.occupancy[x%16][y%16] |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def map_tile_at(x=df.cursor, 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| |  | ||||||
|                 xl = world.map.block_index[xb] |  | ||||||
|                 (0...world.map.y_count_block).each { |yb| |  | ||||||
|                     yl = xl[yb] |  | ||||||
|                     (0...world.map.z_count_block).each { |z| |  | ||||||
|                         p = yl[z] |  | ||||||
|                         yield p if p |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # yields every map block for a given z level |  | ||||||
|         def each_map_block_z(z) |  | ||||||
|             (0...world.map.x_count_block).each { |xb| |  | ||||||
|                 xl = world.map.block_index[xb] |  | ||||||
|                 (0...world.map.y_count_block).each { |yb| |  | ||||||
|                     p = xl[yb][z] |  | ||||||
|                     yield p if p |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         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 offset(dx, dy=nil, dz=0) |  | ||||||
|             if dx.respond_to?(:x) |  | ||||||
|                 dz = dx.z if dx.respond_to?(:z) |  | ||||||
|                 dx, dy = dx.x, dx.y |  | ||||||
|             end |  | ||||||
|             df.map_tile_at(@x+dx, @y+dy, @z+dz) |  | ||||||
|         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 |  | ||||||
| 
 |  | ||||||
|         def shape_caption |  | ||||||
|             TiletypeShape::Caption[shape] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def shape_basic |  | ||||||
|             TiletypeShape::BasicShape[shape] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def shape_passablelow |  | ||||||
|             TiletypeShape::PassableLow[shape] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def shape_passablehigh |  | ||||||
|             TiletypeShape::PassableHigh[shape] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def shape_passableflow |  | ||||||
|             TiletypeShape::PassableFlow[shape] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def shape_walkable |  | ||||||
|             TiletypeShape::Walkable[shape] |  | ||||||
|         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][@dx] > 0 |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the first BlockBurrow this tile is in (nil if none) |  | ||||||
|         def burrow |  | ||||||
|             mapblock.block_burrows.find { |b| |  | ||||||
|                 b.tile_bitmask.bits[@dy][@dx] > 0 |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the array of BlockBurrow this tile is in |  | ||||||
|         def all_burrows |  | ||||||
|             mapblock.block_burrows.find_all { |b| |  | ||||||
|                 b.tile_bitmask.bits[@dy][@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 RegionMapEntry (from designation.biome) |  | ||||||
|         def region_map_entry |  | ||||||
|             b = mapblock.region_offset[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.region_map[rx][ry] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the world_data.geo_biome for current tile |  | ||||||
|         def geo_biome |  | ||||||
|             df.world.world_data.geo_biomes[ region_map_entry.geo_index ] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the world_data.geo_biome.layer for current tile |  | ||||||
|         def stone_layer |  | ||||||
|             geo_biome.layers[designation.geolayer_index] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # MaterialInfo: token for current tile, based on tilemat (vein, soil, plant, lava_stone...) |  | ||||||
|         def mat_info |  | ||||||
|             case tilemat |  | ||||||
|             when :SOIL |  | ||||||
|                 base = stone_layer |  | ||||||
|                 if !df.world.raws.inorganics[base.mat_index].flags[:SOIL_ANY] |  | ||||||
|                     base = geo_biome.layers.find_all { |l| df.world.raws.inorganics[l.mat_index].flags[:SOIL_ANY] }.last |  | ||||||
|                 end |  | ||||||
|                 mat_index = (base ? base.mat_index : rand(df.world.raws.inorganics.length)) |  | ||||||
|                 MaterialInfo.new(0, mat_index) |  | ||||||
| 
 |  | ||||||
|             when :STONE |  | ||||||
|                 base = stone_layer |  | ||||||
|                 if df.world.raws.inorganics[base.mat_index].flags[:SOIL_ANY] |  | ||||||
|                     base = geo_biome.layers.find { |l| !df.world.raws.inorganics[l.mat_index].flags[:SOIL_ANY] } |  | ||||||
|                 end |  | ||||||
|                 mat_index = (base ? base.mat_index : rand(df.world.raws.inorganics.length)) |  | ||||||
|                 MaterialInfo.new(0, mat_index) |  | ||||||
| 
 |  | ||||||
|             when :MINERAL |  | ||||||
|                 mat_index = (mat_index_vein || stone_layer.mat_index) |  | ||||||
|                 MaterialInfo.new(0, mat_index) |  | ||||||
| 
 |  | ||||||
|             when :LAVA_STONE |  | ||||||
|                 # XXX this is wrong |  | ||||||
|                 # maybe should search world.region_details.pos == biome_region_pos ? |  | ||||||
|                 idx = mapblock.region_offset[designation.biome] |  | ||||||
|                 mat_index = df.world.world_data.region_details[idx].lava_stone |  | ||||||
|                 MaterialInfo.new(0, mat_index) |  | ||||||
| 
 |  | ||||||
|             when :FEATURE |  | ||||||
|                 if designation.feature_local |  | ||||||
|                     mx = mapblock.region_pos.x |  | ||||||
|                     my = mapblock.region_pos.y |  | ||||||
|                     df.decode_mat(df.world.world_data.feature_map[mx/16][my/16].features.feature_init[mx%16][my%16][mapblock.local_feature]) |  | ||||||
|                 elsif designation.feature_global |  | ||||||
|                     df.decode_mat(df.world.world_data.underground_regions[mapblock.global_feature].feature_init) |  | ||||||
|                 else |  | ||||||
|                     MaterialInfo.new(-1, -1) |  | ||||||
|                 end |  | ||||||
| 
 |  | ||||||
|             when :FROZEN_LIQUID |  | ||||||
|                 MaterialInfo.new('WATER') |  | ||||||
| 
 |  | ||||||
|             # TODO |  | ||||||
|             #when :PLANT |  | ||||||
|             #when :GRASS_DARK, :GRASS_DEAD, :GRASS_DRY, :GRASS_LIGHT |  | ||||||
|             #when :CONSTRUCTION |  | ||||||
|             else    # AIR ASHES BROOK CAMPFIRE DRIFTWOOD FIRE HFS MAGMA POOL RIVER |  | ||||||
|                 MaterialInfo.new(-1, -1) |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def mat_type |  | ||||||
|             mat_info.mat_type |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def mat_index |  | ||||||
|             mat_info.mat_index |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def inspect |  | ||||||
|             "#<MapTile pos=[#@x, #@y, #@z] shape=#{shape} tilemat=#{tilemat} material=#{mat_info.token}>" |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def dig(mode=:Default) |  | ||||||
|             if mode == :Smooth |  | ||||||
|                 if (tilemat == :STONE or tilemat == :MINERAL) and caption !~ /smooth|pillar|fortification/i and   # XXX caption.. |  | ||||||
|                     designation.smooth == 0 and (designation.hidden or not df.world.jobs.list.find { |j| |  | ||||||
|                         # the game removes 'smooth' designation as soon as it assigns a job, if we |  | ||||||
|                         # re-set it the game may queue another :DetailWall that will carve a fortification |  | ||||||
|                         (j.job_type == :DetailWall or j.job_type == :DetailFloor) and df.same_pos?(j, self) |  | ||||||
|                     }) |  | ||||||
|                     designation.dig = :No |  | ||||||
|                     designation.smooth = 1 |  | ||||||
|                     mapblock.flags.designated = true |  | ||||||
|                 end |  | ||||||
|             else |  | ||||||
|                 return if mode != :No and designation.dig == :No and not designation.hidden and df.world.jobs.list.find { |j| |  | ||||||
|                     # someone already enroute to dig here, avoid 'Inappropriate dig square' spam |  | ||||||
|                     JobType::Type[j.job_type] == :Digging and df.same_pos?(j, self) |  | ||||||
|                 } |  | ||||||
|                 designation.dig = mode |  | ||||||
|                 mapblock.flags.designated = true if mode != :No |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def spawn_liquid(quantity, is_magma=false, flowing=true) |  | ||||||
|             designation.flow_size = quantity |  | ||||||
|             designation.liquid_type = (is_magma ? :Magma : :Water) |  | ||||||
|             designation.flow_forbid = true if is_magma or quantity >= 4 |  | ||||||
| 
 |  | ||||||
|             if flowing |  | ||||||
|                 mapblock.flags.update_liquid = true |  | ||||||
|                 mapblock.flags.update_liquid_twice = true |  | ||||||
| 
 |  | ||||||
|                 zf = df.world.map_extras.z_level_flags[z] |  | ||||||
|                 zf.update = true |  | ||||||
|                 zf.update_twice = true |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def spawn_water(quantity=7) |  | ||||||
|             spawn_liquid(quantity) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def spawn_magma(quantity=7) |  | ||||||
|             spawn_liquid(quantity, true) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # yield a serie of tiles until the block returns true, returns the matching tile |  | ||||||
|         # the yielded tiles form a (squared) spiral centered here in the current zlevel |  | ||||||
|         # eg for radius 4, yields (-4, -4), (-4, -3), .., (-4, 3), |  | ||||||
|         #   (-4, 4), (-3, 4), .., (4, 4), .., (4, -4), .., (-3, -4) |  | ||||||
|         # then move on to radius 5 |  | ||||||
|         def spiral_search(maxradius=100, minradius=0, step=1) |  | ||||||
|             if minradius == 0 |  | ||||||
|                     return self if yield self |  | ||||||
|                     minradius += step |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             sides = [[0, 1], [1, 0], [0, -1], [-1, 0]] |  | ||||||
|             (minradius..maxradius).step(step) { |r| |  | ||||||
|                 sides.length.times { |s| |  | ||||||
|                     dxr, dyr = sides[(s-1) % sides.length] |  | ||||||
|                     dx, dy = sides[s] |  | ||||||
|                     (-r...r).step(step) { |v| |  | ||||||
|                         t = offset(dxr*r + dx*v, dyr*r + dy*v) |  | ||||||
|                         return t if t and yield t |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             nil |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # returns dx^2+dy^2+dz^2 |  | ||||||
|         def distance_to(ot) |  | ||||||
|             (x-ot.x)**2 + (y-ot.y)**2 + (z-ot.z)**2 |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1,203 +0,0 @@ | |||||||
| 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 |  | ||||||
| 
 |  | ||||||
|         def ===(other) |  | ||||||
|             other.mat_index == mat_index and other.mat_type == mat_type |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     class << self |  | ||||||
|         def decode_mat(what, idx=nil) |  | ||||||
|             MaterialInfo.new(what, idx) |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
| @ -1,111 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         # return a Plant |  | ||||||
|         # arg similar to unit.rb/unit_find, no menu |  | ||||||
|         def plant_find(what=cursor) |  | ||||||
|             if what.kind_of?(Integer) |  | ||||||
|                 world.items.all.binsearch(what) |  | ||||||
|             elsif what.respond_to?(:x) or what.respond_to?(:pos) |  | ||||||
|                 world.plants.all.find { |p| same_pos?(what, p) } |  | ||||||
|             else |  | ||||||
|                 raise "what what?" |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def each_tree(material=:any) |  | ||||||
|             @raws_tree_name ||= {} |  | ||||||
|             if @raws_tree_name.empty? |  | ||||||
|                 df.world.raws.plants.all.each_with_index { |p, idx| |  | ||||||
|                     @raws_tree_name[idx] = p.id if p.flags[:TREE] |  | ||||||
|                 } |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             if material != :any |  | ||||||
|                 mat = match_rawname(material, @raws_tree_name.values) |  | ||||||
|                 unless wantmat = @raws_tree_name.index(mat) |  | ||||||
|                     raise "invalid tree material #{material}" |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             world.plants.all.each { |plant| |  | ||||||
|                 next if not @raws_tree_name[plant.material] |  | ||||||
|                 next if wantmat and plant.material != wantmat |  | ||||||
|                 yield plant |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def each_shrub(material=:any) |  | ||||||
|             @raws_shrub_name ||= {} |  | ||||||
|             if @raws_tree_name.empty? |  | ||||||
|                 df.world.raws.plants.all.each_with_index { |p, idx| |  | ||||||
|                     @raws_shrub_name[idx] = p.id if not p.flags[:GRASS] and not p.flags[:TREE] |  | ||||||
|                 } |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             if material != :any |  | ||||||
|                 mat = match_rawname(material, @raws_shrub_name.values) |  | ||||||
|                 unless wantmat = @raws_shrub_name.index(mat) |  | ||||||
|                     raise "invalid shrub material #{material}" |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         SaplingToTreeAge = 120960 |  | ||||||
|         def cuttrees(material=nil, count_max=100, quiet=false) |  | ||||||
|             if !material |  | ||||||
|                 # list trees |  | ||||||
|                 cnt = Hash.new(0) |  | ||||||
|                 each_tree { |plant| |  | ||||||
|                     next if plant.grow_counter < SaplingToTreeAge |  | ||||||
|                     next if map_designation_at(plant).hidden |  | ||||||
|                     cnt[plant.material] += 1 |  | ||||||
|                 } |  | ||||||
|                 cnt.sort_by { |mat, c| c }.each { |mat, c| |  | ||||||
|                     name = @raws_tree_name[mat] |  | ||||||
|                     puts " #{name} #{c}" unless quiet |  | ||||||
|                 } |  | ||||||
|             else |  | ||||||
|                 cnt = 0 |  | ||||||
|                 each_tree(material) { |plant| |  | ||||||
|                     next if plant.grow_counter < SaplingToTreeAge |  | ||||||
|                     b = map_block_at(plant) |  | ||||||
|                     d = b.designation[plant.pos.x%16][plant.pos.y%16] |  | ||||||
|                     next if d.hidden |  | ||||||
|                     if d.dig == :No |  | ||||||
|                         d.dig = :Default |  | ||||||
|                         b.flags.designated = true |  | ||||||
|                         cnt += 1 |  | ||||||
|                         break if cnt == count_max |  | ||||||
|                     end |  | ||||||
|                 } |  | ||||||
|                 puts "Updated #{cnt} plant designations" unless quiet |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def growtrees(material=nil, count_max=100, quiet=false) |  | ||||||
|             if !material |  | ||||||
|                 # list plants |  | ||||||
|                 cnt = Hash.new(0) |  | ||||||
|                 each_tree { |plant| |  | ||||||
|                     next if plant.grow_counter >= SaplingToTreeAge |  | ||||||
|                     next if map_designation_at(plant).hidden |  | ||||||
|                     cnt[plant.material] += 1 |  | ||||||
|                 } |  | ||||||
|                 cnt.sort_by { |mat, c| c }.each { |mat, c| |  | ||||||
|                     name = @raws_tree_name[mat] |  | ||||||
|                     puts " #{name} #{c}" unless quiet |  | ||||||
|                 } |  | ||||||
|             else |  | ||||||
|                 cnt = 0 |  | ||||||
|                 each_tree(material) { |plant| |  | ||||||
|                     next if plant.grow_counter >= SaplingToTreeAge |  | ||||||
|                     next if map_designation_at(plant).hidden |  | ||||||
|                     plant.grow_counter = SaplingToTreeAge |  | ||||||
|                     cnt += 1 |  | ||||||
|                     break if cnt == count_max |  | ||||||
|                 } |  | ||||||
|                 puts "Grown #{cnt} saplings" unless quiet |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -1,258 +0,0 @@ | |||||||
| # redefine standard i/o methods to use the dfhack console |  | ||||||
| module Kernel |  | ||||||
|     def puts(*a) |  | ||||||
|         a.flatten.each { |l| |  | ||||||
|             # XXX looks like print_str crashes with strings longer than 4096... maybe not nullterminated ? |  | ||||||
|             # this workaround fixes it |  | ||||||
|             s = l.to_s.chomp + "\n" |  | ||||||
|             while s.length > 0 |  | ||||||
|                 DFHack.print_str(s[0, 4000]) |  | ||||||
|                 s[0, 4000] = '' |  | ||||||
|             end |  | ||||||
|         } |  | ||||||
|         nil |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def puts_err(*a) |  | ||||||
|         a.flatten.each { |l| |  | ||||||
|             s = l.to_s.chomp + "\n" |  | ||||||
|             while s.length > 0 |  | ||||||
|                 DFHack.print_err(s[0, 4000]) |  | ||||||
|                 s[0, 4000] = '' |  | ||||||
|             end |  | ||||||
|         } |  | ||||||
|         nil |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def p(*a) |  | ||||||
|         a.each { |e| |  | ||||||
|             puts_err e.inspect |  | ||||||
|         } |  | ||||||
|         nil |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| module DFHack |  | ||||||
|     VERSION = version |  | ||||||
| 
 |  | ||||||
|     class OnupdateCallback |  | ||||||
|         attr_accessor :callback, :timelimit, :minyear, :minyeartick, :description |  | ||||||
|         def initialize(descr, cb, tl, initdelay=0) |  | ||||||
|             @description = descr |  | ||||||
|             @callback = cb |  | ||||||
|             @ticklimit = tl |  | ||||||
|             @minyear = (tl ? df.cur_year : 0) |  | ||||||
|             @minyeartick = (tl ? df.cur_year_tick+initdelay : 0) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # run callback if timedout |  | ||||||
|         def check_run(year, yeartick, yearlen) |  | ||||||
|             if @ticklimit |  | ||||||
|                 return unless year > @minyear or (year == @minyear and yeartick >= @minyeartick) |  | ||||||
|                 @minyear = year |  | ||||||
|                 @minyeartick = yeartick + @ticklimit |  | ||||||
|                 if @minyeartick > yearlen |  | ||||||
|                     @minyear += 1 |  | ||||||
|                     @minyeartick -= yearlen |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|             # t0 = Time.now |  | ||||||
|             @callback.call |  | ||||||
|             # dt = Time.now - t0 ; puts "rb cb #@description took #{'%.02f' % dt}s" if dt > 0.1 |  | ||||||
|         rescue Exception |  | ||||||
|             df.onupdate_unregister self |  | ||||||
|             puts_err "onupdate #@description unregistered: #$!", $!.backtrace |  | ||||||
|         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('fastdwarf') { DFHack.world.units[0].counters.job_counter = 0 } |  | ||||||
|         # if ticklimit is given, do not call unless this much game ticks have passed. Handles advmode time stretching. |  | ||||||
|         def onupdate_register(descr, ticklimit=nil, initialtickdelay=0, &b) |  | ||||||
|             raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String) |  | ||||||
|             @onupdate_list ||= [] |  | ||||||
|             @onupdate_list << OnupdateCallback.new(descr, b, ticklimit, initialtickdelay) |  | ||||||
|             DFHack.onupdate_active = true |  | ||||||
|             if onext = @onupdate_list.min |  | ||||||
|                 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 or the description |  | ||||||
|         def onupdate_unregister(b) |  | ||||||
|             b = @onupdate_list.find { |bb| bb.description == b } if b.kind_of?(String) |  | ||||||
|             @onupdate_list.delete b |  | ||||||
|             if @onupdate_list.empty? |  | ||||||
|                 DFHack.onupdate_active = false |  | ||||||
|                 DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = DFHack.onupdate_minyeartickadv = -1 |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # same as onupdate_register, but remove the callback once it returns true |  | ||||||
|         def onupdate_register_once(*a) |  | ||||||
|             handle = onupdate_register(*a) { |  | ||||||
|                 onupdate_unregister(handle) if yield |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         TICKS_PER_YEAR = 1200*28*12 |  | ||||||
|         # this method is called by ruby.cpp if df.onupdate_active is true |  | ||||||
|         def onupdate |  | ||||||
|             @onupdate_list ||= [] |  | ||||||
| 
 |  | ||||||
|             y = yt = 0 |  | ||||||
|             y = cur_year rescue 0 |  | ||||||
|             ytmax = TICKS_PER_YEAR |  | ||||||
|             if df.gamemode == :ADVENTURE and df.respond_to?(:cur_year_tick_advmode) |  | ||||||
|                 yt = cur_year_tick_advmode |  | ||||||
|                 ytmax *= 144 |  | ||||||
|             else |  | ||||||
|                 yt = cur_year_tick rescue 0 |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             @onupdate_list.each { |o| |  | ||||||
|                 o.check_run(y, yt, ytmax) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if onext = @onupdate_list.min |  | ||||||
|                 DFHack.onupdate_minyear = onext.minyear |  | ||||||
|                 if ytmax > TICKS_PER_YEAR |  | ||||||
|                     DFHack.onupdate_minyeartick = -1 |  | ||||||
|                     DFHack.onupdate_minyeartickadv = onext.minyeartick |  | ||||||
|                 else |  | ||||||
|                     DFHack.onupdate_minyeartick = onext.minyeartick |  | ||||||
|                     DFHack.onupdate_minyeartickadv = -1 |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # register a callback to be called every gframe or more |  | ||||||
|         # ex: DFHack.onstatechange_register { |newstate| puts "state changed to #{newstate}" } |  | ||||||
|         def onstatechange_register(&b) |  | ||||||
|             @onstatechange_list ||= [] |  | ||||||
|             @onstatechange_list << b |  | ||||||
|             @onstatechange_list.last |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # delete the callback for onstatechange ; use the value returned by onstatechange_register |  | ||||||
|         def onstatechange_unregister(b) |  | ||||||
|             @onstatechange_list.delete b |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # same as onstatechange_register, but auto-unregisters if the block returns true |  | ||||||
|         def onstatechange_register_once |  | ||||||
|             handle = onstatechange_register { |st| |  | ||||||
|                 onstatechange_unregister(handle) if yield(st) |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         # this method is called by dfhack every 'onstatechange' |  | ||||||
|         def onstatechange(newstate) |  | ||||||
|             @onstatechange_list ||= [] |  | ||||||
|             @onstatechange_list.each { |cb| cb.call(newstate) } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return true if the argument is under the cursor |  | ||||||
|         def at_cursor?(obj) |  | ||||||
|             same_pos?(obj, cursor) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # returns true if both arguments are at the same x/y/z |  | ||||||
|         def same_pos?(pos1, pos2) |  | ||||||
|             pos1 = pos1.pos if pos1.respond_to?(:pos) |  | ||||||
|             pos2 = pos2.pos if pos2.respond_to?(:pos) |  | ||||||
|             pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # try to match a user-specified name to one from the raws |  | ||||||
|         # uses case-switching and substring matching |  | ||||||
|         # eg match_rawname('coal', ['COAL_BITUMINOUS', 'BAUXITE']) => 'COAL_BITUMINOUS' |  | ||||||
|         def match_rawname(name, rawlist) |  | ||||||
|             rawlist.each { |r| return r if name == r } |  | ||||||
|             rawlist.each { |r| return r if name.downcase == r.downcase } |  | ||||||
|             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[gametype] |  | ||||||
|                     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 |  | ||||||
|                     out << wl.words[name.words[2]].forms[name.parts_of_speech[2]] if name.words[2] >= 0 |  | ||||||
|                     out << wl.words[name.words[3]].forms[name.parts_of_speech[3]] if name.words[3] >= 0 |  | ||||||
|                     if name.words[4] >= 0 |  | ||||||
|                         out << wl.words[name.words[4]].forms[name.parts_of_speech[4]] |  | ||||||
|                         out.last << '-' |  | ||||||
|                     else |  | ||||||
|                         out << '' |  | ||||||
|                     end |  | ||||||
|                     out.last << wl.words[name.words[5]].forms[name.parts_of_speech[5]] |  | ||||||
|                 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 |  | ||||||
| 
 |  | ||||||
| # global alias so we can write 'df.world.units.all[0]' |  | ||||||
| def df |  | ||||||
|     DFHack |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| # load autogenned file |  | ||||||
| require './hack/ruby/ruby-autogen-defs' |  | ||||||
| require(RUBY_PLATFORM =~ /mswin|mingw|cygwin/i ? './hack/ruby/ruby-autogen-win' : './hack/ruby/ruby-autogen-gcc') |  | ||||||
| 
 |  | ||||||
| # load all modules |  | ||||||
| Dir['./hack/ruby/*.rb'].each { |m| require m.chomp('.rb') if m !~ /ruby-autogen/ } |  | ||||||
| @ -1,91 +0,0 @@ | |||||||
| # 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) |  | ||||||
|             x = x.pos if x.respond_to?(:pos) |  | ||||||
|             x, y, z = x.x, x.y, x.z if x.respond_to?(:x) |  | ||||||
| 
 |  | ||||||
|             # compute screen 'map' size (tiles) |  | ||||||
|             menuwidth = ui_menu_width[0] |  | ||||||
|             # ui_menu_width shows only the 'tab' status |  | ||||||
|             menuwidth = 1 if menuwidth == 2 and ui_menu_width[1] == 2 and cursor.x != -30000 |  | ||||||
|             menuwidth = 2 if menuwidth == 3 and cursor.x != -30000 |  | ||||||
|             w_w = gps.dimx - 2 |  | ||||||
|             w_h = gps.dimy - 2 |  | ||||||
|             case menuwidth |  | ||||||
|             when 1; w_w -= 55 |  | ||||||
|             when 2; w_w -= (ui_menu_width[1] == 2 ? 24 : 31) |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             # center view |  | ||||||
|             w_x = x - w_w/2 |  | ||||||
|             w_y = y - w_h/2 |  | ||||||
|             w_z = z |  | ||||||
|             # round view coordinates (optional) |  | ||||||
|             #w_x -= w_x % 10 |  | ||||||
|             #w_y -= w_y % 10 |  | ||||||
|             # crop to map limits |  | ||||||
|             w_x = [[w_x, world.map.x_count - w_w].min, 0].max |  | ||||||
|             w_y = [[w_y, world.map.y_count - w_h].min, 0].max |  | ||||||
| 
 |  | ||||||
|             self.window_x = w_x |  | ||||||
|             self.window_y = w_y |  | ||||||
|             self.window_z = w_z |  | ||||||
| 
 |  | ||||||
|             if cursor.x != -30000 |  | ||||||
|                 cursor.x, cursor.y, cursor.z = x, y, z |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # add an announcement |  | ||||||
|         # color = integer, bright = bool |  | ||||||
|         def add_announcement(str, color=nil, bright=nil) |  | ||||||
|             cont = false |  | ||||||
|             while str.length > 0 |  | ||||||
|                 rep = Report.cpp_new |  | ||||||
|                 rep.color = color if color |  | ||||||
|                 rep.bright = ((bright && bright != 0) ? 1 : 0) if bright != nil |  | ||||||
|                 rep.year = cur_year |  | ||||||
|                 rep.time = cur_year_tick |  | ||||||
|                 rep.flags.continuation = cont |  | ||||||
|                 cont = true |  | ||||||
|                 rep.flags.announcement = true |  | ||||||
|                 rep.text = str[0, 73] |  | ||||||
|                 str = str[73..-1].to_s |  | ||||||
|                 rep.id = world.status.next_report_id |  | ||||||
|                 world.status.next_report_id += 1 |  | ||||||
|                 world.status.reports << rep |  | ||||||
|                 world.status.announcements << rep |  | ||||||
|                 world.status.display_timer = 2000 |  | ||||||
|                 yield rep if block_given? |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # add an announcement to display in a game popup message |  | ||||||
|         # (eg "the megabeast foobar arrived") |  | ||||||
|         def popup_announcement(str, color=nil, bright=nil) |  | ||||||
|             pop = PopupMessage.cpp_new(:text => str) |  | ||||||
|             pop.color = color if color |  | ||||||
|             pop.bright = bright if bright |  | ||||||
|             world.status.popups << pop |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     class Viewscreen |  | ||||||
|         def feed_keys(*keys) |  | ||||||
|             keyset = StlSet.cpp_new(keys, InterfaceKey) |  | ||||||
|             ret = feed(keyset) |  | ||||||
|             keyset._cpp_delete |  | ||||||
|             ret |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1,280 +0,0 @@ | |||||||
| module DFHack |  | ||||||
|     class << self |  | ||||||
|         # return an Unit |  | ||||||
|         # 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, y=nil, z=nil) |  | ||||||
|             if what == :selected |  | ||||||
|                 return world.units.all.binsearch(df.get_selected_unit_id) |  | ||||||
|             elsif what.kind_of?(Integer) |  | ||||||
|                 # 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 |  | ||||||
|                 raise "what what?" |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # returns an Array of all units that are current fort citizen (dwarves, on map, not hostile) |  | ||||||
|         def unit_citizens |  | ||||||
|             world.units.active.find_all { |u| |  | ||||||
|                 unit_iscitizen(u) |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_testflagcurse(u, flag) |  | ||||||
|             return false if u.curse.rem_tags1.send(flag) |  | ||||||
|             return true if u.curse.add_tags1.send(flag) |  | ||||||
|             return false if u.caste < 0 |  | ||||||
|             u.race_tg.caste[u.caste].flags[flag] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_isfortmember(u) |  | ||||||
|             # RE from viewscreen_unitlistst ctor |  | ||||||
|             return false if df.gamemode != :DWARF or |  | ||||||
|                     u.mood == :Berserk or |  | ||||||
|                     unit_testflagcurse(u, :CRAZED) or |  | ||||||
|                     unit_testflagcurse(u, :OPPOSED_TO_LIFE) or |  | ||||||
|                     u.enemy.undead or |  | ||||||
|                     u.flags3.ghostly or |  | ||||||
|                     u.flags1.marauder or u.flags1.active_invader or u.flags1.invader_origin or |  | ||||||
|                     u.flags1.forest or |  | ||||||
|                     u.flags1.merchant or u.flags1.diplomat |  | ||||||
|             return true if u.flags1.tame |  | ||||||
|             return false if u.flags2.underworld or u.flags2.resident or |  | ||||||
|                     u.flags2.visitor_uninvited or u.flags2.visitor or |  | ||||||
|                     u.civ_id == -1 or |  | ||||||
|                     u.civ_id != df.ui.civ_id |  | ||||||
|             true |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the page in viewscreen_unitlist where the unit would appear |  | ||||||
|         def unit_category(u) |  | ||||||
|             return if u.flags1.left or u.flags1.incoming |  | ||||||
|             # return if hostile & unit_invisible(u) (hidden_in_ambush or caged+mapblock.hidden or caged+holder.ambush |  | ||||||
|             return :Dead if u.flags2.killed |  | ||||||
|             return :Dead if u.flags3.ghostly # hostile ? |  | ||||||
|             return if u.flags1.inactive |  | ||||||
|             return :Others if !unit_isfortmember(u) |  | ||||||
|             casteflags = u.race_tg.caste[u.caste].flags if u.caste >= 0 |  | ||||||
|             return :Livestock if casteflags and (casteflags[:PET] or casteflags[:PET_EXOTIC]) |  | ||||||
|             return :Citizens if unit_testflagcurse(u, :CAN_SPEAK) |  | ||||||
|             :Livestock |  | ||||||
|             # some other stuff with ui.race_id ? (jobs only?) |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # merchant: df.ui.caravans.find { |cv| cv.entity == u.civ_id } |  | ||||||
|         # diplomat: df.ui.dip_meeting_info.find { |m| m.diplomat_id == u.hist_figure_id or m.diplomat_id2 == u.hist_figure_id } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         def unit_nemesis(u) |  | ||||||
|             if ref = u.general_refs.find { |r| r.kind_of?(DFHack::GeneralRefIsNemesisst) } |  | ||||||
|                 ref.nemesis_tg |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # return the subcategory for :Others (from vs_unitlist) |  | ||||||
|         def unit_other_category(u) |  | ||||||
|             # comment is actual code returned by the df function |  | ||||||
|             return :Berserk if u.mood == :Berserk  # 5 |  | ||||||
|             return :Berserk if unit_testflagcurse(u, :CRAZED)    # 14 |  | ||||||
|             return :Undead if unit_testflagcurse(u, :OPPOSED_TO_LIFE)   # 1 |  | ||||||
|             return :Undead if u.flags3.ghostly  # 15 |  | ||||||
| 
 |  | ||||||
|             if df.gamemode == :ADVENTURE |  | ||||||
|                 return :Hostile if u.civ_id == -1   # 2 |  | ||||||
|                 if u.animal.population.region_x == -1 |  | ||||||
|                     return :Wild if u.flags2.roaming_wilderness_population_source_not_a_map_feature # 0 |  | ||||||
|                 else |  | ||||||
|                     return :Hostile if u.flags2.important_historical_figure and n = unit_nemesis(u) and n.flags[:ACTIVE_ADVENTURER] # 2 |  | ||||||
|                 end |  | ||||||
|                 return :Hostile if u.flags2.resident    # 3 |  | ||||||
|                 return :Hostile # 4 |  | ||||||
|             end |  | ||||||
| 
 |  | ||||||
|             return :Invader if u.flags1.active_invader or u.flags1.invader_origin   # 6 |  | ||||||
|             return :Friendly if u.flags1.forest or u.flags1.merchant or u.flags1.diplomat   # 8 |  | ||||||
|             return :Hostile if u.flags1.tame    # 7 |  | ||||||
| 
 |  | ||||||
|             if u.civ_id != -1 |  | ||||||
|                 return :Unsure if u.civ_id != df.ui.civ_id or u.flags1.resident or u.flags1.visitor or u.flags1.visitor_uninvited   # 10 |  | ||||||
|                 return :Hostile # 7 |  | ||||||
| 
 |  | ||||||
|             elsif u.animal.population.region_x == -1 |  | ||||||
|                 return :Friendly if u.flags2.visitor    # 8 |  | ||||||
|                 return :Uninvited if u.flags2.visitor_uninvited # 12 |  | ||||||
|                 return :Underworld if r = u.race_tg and r.underground_layer_min == 5    # 9 |  | ||||||
|                 return :Resident if u.flags2.resident   # 13 |  | ||||||
|                 return :Friendly    # 8 |  | ||||||
| 
 |  | ||||||
|             else |  | ||||||
|                 return :Friendly if u.flags2.visitor    # 8 |  | ||||||
|                 return :Underworld if r = u.race_tg and r.underground_layer_min == 5    # 9 |  | ||||||
|                 return :Wild if u.animal.population.feature_idx == -1 and u.animal.population.cave_id == -1 # 0 |  | ||||||
|                 return :Wild    # 11 |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_iscitizen(u) |  | ||||||
|             unit_category(u) == :Citizens |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_hostiles |  | ||||||
|             world.units.active.find_all { |u| |  | ||||||
|                 unit_ishostile(u) |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # returns if an unit is openly hostile |  | ||||||
|         # does not include ghosts / wildlife |  | ||||||
|         def unit_ishostile(u) |  | ||||||
|             # return true if u.flags3.ghostly and not u.flags1.inactive |  | ||||||
|             return false unless unit_category(u) == :Others |  | ||||||
| 
 |  | ||||||
|             case unit_other_category(u) |  | ||||||
|             when :Berserk, :Undead, :Hostile, :Invader, :Underworld |  | ||||||
|                 # XXX :Resident, :Uninvited? |  | ||||||
|                 true |  | ||||||
| 
 |  | ||||||
|             when :Unsure |  | ||||||
|                 # from df code, with removed duplicate checks already in other_category |  | ||||||
|                 return true if u.enemy.undead or u.flags3.ghostly or u.flags1.marauder |  | ||||||
|                 return false if u.flags1.forest or u.flags1.merchant or u.flags1.diplomat or u.flags2.visitor |  | ||||||
|                 return true if u.flags1.tame or u.flags2.underworld |  | ||||||
| 
 |  | ||||||
|                 if histfig = u.hist_figure_tg |  | ||||||
|                     group = df.ui.group_tg |  | ||||||
|                     case unit_checkdiplomacy_hf_ent(histfig, group) |  | ||||||
|                     when 4, 5 |  | ||||||
|                         true |  | ||||||
|                     else |  | ||||||
|                         false |  | ||||||
|                     end |  | ||||||
| 
 |  | ||||||
|                 elsif diplo = u.civ_tg.unknown1b.diplomacy.binsearch(df.ui.group_id, :group_id) |  | ||||||
|                     diplo.relation != 1 and diplo.relation != 5 |  | ||||||
| 
 |  | ||||||
|                 else |  | ||||||
|                     u.animal.population.region_x != -1 or u.flags2.resident or u.flags2.visitor_uninvited |  | ||||||
|                 end |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_checkdiplomacy_hf_ent(histfig, group) |  | ||||||
|             var_3d = var_3e = var_45 = var_46 = var_47 = var_48 = var_49 = nil |  | ||||||
| 
 |  | ||||||
|             var_3d = 1 if group.type == :Outcast or group.type == :NomadicGroup or |  | ||||||
|             (group.type == :Civilization and group.entity_raw.flags[:LOCAL_BANDITRY]) |  | ||||||
| 
 |  | ||||||
|             histfig.entity_links.each { |link| |  | ||||||
|                 if link.entity_id == group.id |  | ||||||
|                     case link.getType |  | ||||||
|                     when :MEMBER, :MERCENARY, :SLAVE, :PRISONER, :POSITION, :HERO |  | ||||||
|                         var_47 = 1 |  | ||||||
|                     when :FORMER_MEMBER, :FORMER_MERCENARY, :FORMER_SLAVE, :FORMER_PRISONER |  | ||||||
|                         var_48 = 1 |  | ||||||
|                     when :ENEMY |  | ||||||
|                         var_49 = 1 |  | ||||||
|                     when :CRIMINAL |  | ||||||
|                         var_45 = 1 |  | ||||||
|                     end |  | ||||||
|                 else |  | ||||||
|                     case link.getType |  | ||||||
|                     when :MEMBER, :MERCENARY, :SLAVE |  | ||||||
|                         if link_entity = link.entity_tg |  | ||||||
|                             diplo = group.unknown1b.diplomacy.binsearch(link.entity_id, :group_id) |  | ||||||
|                             case diplo.relation |  | ||||||
|                             when 0, 3, 4 |  | ||||||
|                                 var_48 = 1 |  | ||||||
|                             when 1, 5 |  | ||||||
|                                 var_46 = 1 |  | ||||||
|                             end |  | ||||||
| 
 |  | ||||||
|                             var_3e = 1 if link_entity.type == :Outcast or link_entity.type == :NomadicGroup or |  | ||||||
|                             (link_entity.type == :Civilization and link_entity.entity_raw.flags[:LOCAL_BANDITRY]) |  | ||||||
|                         end |  | ||||||
|                     end |  | ||||||
|                 end |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if var_49 |  | ||||||
|                 4 |  | ||||||
|             elsif var_46 |  | ||||||
|                 5 |  | ||||||
|             elsif !var_47 and group.resources.ethic[:KILL_NEUTRAL] == 16 |  | ||||||
|                 4 |  | ||||||
|             elsif df.gamemode == :ADVENTURE and !var_47 and (var_3e or !var_3d) |  | ||||||
|                 4 |  | ||||||
|             elsif var_45 |  | ||||||
|                 3 |  | ||||||
|             elsif var_47 |  | ||||||
|                 2 |  | ||||||
|             elsif var_48 |  | ||||||
|                 1 |  | ||||||
|             else |  | ||||||
|                 0 |  | ||||||
|             end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         # list workers (citizen, not crazy / child / inmood / noble) |  | ||||||
|         def unit_workers |  | ||||||
|             world.units.active.find_all { |u| |  | ||||||
|                 unit_isworker(u) |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_isworker(u) |  | ||||||
|             unit_iscitizen(u) and |  | ||||||
|             u.race == df.ui.race_id and |  | ||||||
|             u.mood == :None and |  | ||||||
|             u.profession != :CHILD and |  | ||||||
|             u.profession != :BABY and |  | ||||||
|             # TODO MENIAL_WORK_EXEMPTION_SPOUSE |  | ||||||
|             !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # list currently idle workers |  | ||||||
|         def unit_idlers |  | ||||||
|             world.units.active.find_all { |u| |  | ||||||
|                 unit_isidler(u) |  | ||||||
|             } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_isidler(u) |  | ||||||
|             unit_isworker(u) and |  | ||||||
|             # current_job includes eat/drink/sleep/pickupequip |  | ||||||
|             !u.job.current_job and |  | ||||||
|             # filter 'attend meeting' |  | ||||||
|             not u.specific_refs.find { |s| s.type == :ACTIVITY } and |  | ||||||
|             # filter soldiers (TODO check schedule) |  | ||||||
|             u.military.squad_id == -1 and |  | ||||||
|             # filter incoming migrants |  | ||||||
|             not u.status.misc_traits.find { |t| t.id == :Migrant } |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         def unit_entitypositions(unit) |  | ||||||
|             list = [] |  | ||||||
|             return list if not histfig = unit.hist_figure_tg |  | ||||||
|             histfig.entity_links.each { |el| |  | ||||||
|                 next if el._rtti_classname != :histfig_entity_link_positionst |  | ||||||
|                 next if not ent = el.entity_tg |  | ||||||
|                 next if not pa = ent.positions.assignments.binsearch(el.assignment_id) |  | ||||||
|                 next if not pos = ent.positions.own.binsearch(pa.position_id) |  | ||||||
|                 list << pos |  | ||||||
|             } |  | ||||||
|             list |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     class LanguageName |  | ||||||
|         def to_s(english=false) |  | ||||||
|             df.translate_name(self, english) |  | ||||||
|         end |  | ||||||
|     end |  | ||||||
| end |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
| @ -1 +0,0 @@ | |||||||
| libruby* |  | ||||||
		Loading…
	
		Reference in New Issue