diff --git a/.gitignore b/.gitignore index 85fc083e7..bdb9474bb 100644 --- a/.gitignore +++ b/.gitignore @@ -50,12 +50,6 @@ dfhack/python/PyDFHack.egg-info dfhack/python/build dfhack/python/dist -# Ruby binding binaries -plugins/ruby/libruby* -plugins/ruby/msvcrtruby*.tar.gz -plugins/ruby/ruby-autogen.rb -plugins/ruby/ruby-autogen.rb.rule - # CPack stuff build/CPack*Config.cmake diff --git a/README.rst b/README.rst index 724fa1fa3..6c463911a 100644 --- a/README.rst +++ b/README.rst @@ -1401,18 +1401,30 @@ Kills any unit of a given race. With no argument, lists the available races. +With the special argument 'him', targets only the selected creature. + Any non-dead non-caged unit of the specified race gets its ``blood_count`` -set to 0, which means immediate death at the next game tick. May not work -on vampires and other weird creatures. +set to 0, which means immediate death at the next game tick. For creatures +such as vampires, also set animal.vanish_countdown to 2. + +An alternate mode is selected by adding a 2nd argument to the command, +``magma``. In this case, a column of 7/7 magma is generated on top of the +targets until they die (Warning: do not call on magma-safe creatures. Also, +using this mode for birds is not recommanded.) + +Will target any unit on a revealed tile of the map, including ambushers. -Targets any unit on a revealed tile of the map, including ambushers. Ex: +Ex: :: slayrace gob -To kill a single creature in the same way, you can use the following line, -after selecting the unit with the 'v' cursor: +To kill a single creature, select the unit with the 'v' cursor and: +:: + slayrace him + +To purify all elves on the map with fire (may have side-effects): :: - rb_eval df.unit_find.body.blood_count = 0 + slayrace elve magma magmasource diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 6b4de736d..882d9527d 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -62,7 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // George Vulov for MacOSX #ifndef __LINUX__ -#define TEMP_FAILURE_RETRY(expr) \ +#define TMP_FAILURE_RETRY(expr) \ ({ long int _res; \ do _res = (long int) (expr); \ while (_res == -1L && errno == EINTR); \ @@ -155,7 +155,7 @@ namespace DFHack FD_ZERO(&descriptor_set); FD_SET(STDIN_FILENO, &descriptor_set); FD_SET(exit_pipe[0], &descriptor_set); - int ret = TEMP_FAILURE_RETRY( + int ret = TMP_FAILURE_RETRY( select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL) ); if(ret == -1) @@ -165,7 +165,7 @@ namespace DFHack if (FD_ISSET(STDIN_FILENO, &descriptor_set)) { // read byte from stdin - ret = TEMP_FAILURE_RETRY( + ret = TMP_FAILURE_RETRY( read(STDIN_FILENO, &out, 1) ); if(ret == -1) @@ -245,7 +245,8 @@ namespace DFHack if(rawmode) { const char * clr = "\033c\033[3J\033[H"; - ::write(STDIN_FILENO,clr,strlen(clr)); + if (::write(STDIN_FILENO,clr,strlen(clr)) == -1) + ; } else { @@ -269,7 +270,8 @@ namespace DFHack { const char * colstr = getANSIColor(index); int lstr = strlen(colstr); - ::write(STDIN_FILENO,colstr,lstr); + if (::write(STDIN_FILENO,colstr,lstr) == -1) + ; } } /// Reset color to default @@ -656,7 +658,8 @@ bool Console::init(bool sharing) inited = false; return false; } - freopen("stdout.log", "w", stdout); + if (!freopen("stdout.log", "w", stdout)) + ; d = new Private(); // make our own weird streams so our IO isn't redirected d->dfout_C = fopen("/dev/tty", "w"); @@ -664,7 +667,8 @@ bool Console::init(bool sharing) clear(); d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO); // init the exit mechanism - pipe(d->exit_pipe); + if (pipe(d->exit_pipe) == -1) + ; FD_ZERO(&d->descriptor_set); FD_SET(STDIN_FILENO, &d->descriptor_set); FD_SET(d->exit_pipe[0], &d->descriptor_set); diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index a9a85636c..e10ee38bf 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,29 +1,29 @@ OPTION(DL_RUBY "download libruby from the internet" ON) IF (DL_RUBY AND NOT APPLE) IF (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/libruby187.tar.gz EXPECTED_MD5 eb2adea59911f68e6066966c1352f291) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - FILE(RENAME libruby1.8.so.1.8.7 libruby.so) - SET(RUBYLIB libruby.so) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + FILE(RENAME ${CMAKE_CURRENT_BINARY_DIR}/libruby1.8.so.1.8.7 ${CMAKE_CURRENT_BINARY_DIR}/libruby.so) + SET(RUBYLIB ${CMAKE_CURRENT_BINARY_DIR}/libruby.so) ELSE (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/msvcrtruby187.tar.gz EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - FILE(RENAME msvcrt-ruby18.dll libruby.dll) - SET(RUBYLIB libruby.dll) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + FILE(RENAME ${CMAKE_CURRENT_BINARY_DIR}/msvcrt-ruby18.dll ${CMAKE_CURRENT_BINARY_DIR}/libruby.dll) + SET(RUBYLIB ${CMAKE_CURRENT_BINARY_DIR}/libruby.dll) ENDIF(UNIX) ENDIF(DL_RUBY AND NOT APPLE) ADD_CUSTOM_COMMAND( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl COMMENT ruby-autogen.rb ) -ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb) +ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb) INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread") @@ -32,6 +32,8 @@ ADD_DEPENDENCIES(ruby ruby-autogen-rb) INSTALL(FILES ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DESTINATION hack/ruby) + INSTALL(DIRECTORY . DESTINATION hack/ruby FILES_MATCHING PATTERN "*.rb") diff --git a/plugins/ruby/README b/plugins/ruby/README index 97a4cbd30..8a473f332 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -23,7 +23,7 @@ All ruby code runs while the main DF process and other plugins are suspended. DFHack console -------------- -The ruby plugin defines 1 dfhack console command: +The ruby plugin defines one new dfhack console command: rb_eval ; 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 @@ -50,7 +50,7 @@ The script can access the console command arguments through the global variable '$script_args', which is an array of ruby Strings. The help string displayed in dfhack 'ls' command is the first line of the -script, if it is a comment (starts with '# '). +script, if it is a comment (ie starts with '# '). Ruby helper functions @@ -67,7 +67,9 @@ obj1 and 2 should respond to #pos and #x #y #z. Returns the MapBlock for the coordinates or nil. df.map_tile_at(pos) -Returns a MapTile, holds all information relative to the map tile. +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| } @@ -142,9 +144,16 @@ The ruby classes defined in ruby-autogen.rb are accessors to the underlying df C++ objects in-memory. To allocate a new C++ object for use in DF, use the RubyClass.cpp_new method (see buildings.rb for exemples), works for Compounds only. +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 is not supported. You may manually call df.free if you know -what you are doing (maps directly to the native malloc/free) +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' @@ -160,11 +169,13 @@ To delete an element, vector.delete_at(index) You can binary search an element in a vector for a given numeric field value: df.world.unit.all.binsearch(42, :id) -will find the element whose 'id' field is 42 (needs the vector to be initially +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 4 arguments. Arguments / return value are interpreted as Compound/Enums as @@ -194,7 +205,7 @@ Change current unit profession Center the screen on unit ID '123' df.center_viewscreen(df.unit_find(123)) -Find an item at a given position, show its C++ classname +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 @@ -205,15 +216,29 @@ Dig a channel under the cursor df.map_designation_at(df.cursor).dig = :Channel df.map_block_at(df.cursor).flags.designated = true +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 *.rb file including user comfort functions and -describing basic classes used by the autogenerated code, and ruby-autogen.rb, -the auto-generated code. +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. -autogen is output by codegen.pl from dfhack/library/include/df/codegen.out.xml +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 exemple, @@ -230,6 +255,7 @@ Will generate 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, diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 9a59411f9..ab029ac24 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -286,14 +286,23 @@ module DFHack job end + # check item flags to see if it is suitable for use as a building material + def building_isitemfree(i) + !i.flags.in_job and + !i.flags.in_inventory and + !i.flags.removed and + !i.flags.in_building and + !i.flags.owned and + !i.flags.forbid + 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 - i.itemrefs.empty? and - !i.flags.in_job + building_isitemfree(i) } raise 'no free bed, build more !' if not item diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index cbe7c932a..593216d71 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -175,10 +175,10 @@ sub render_bitfield_fields { if ($name) { - if ($count == 1) { - push @lines_rb, "field(:$name, 0) { bit $shift }"; - } elsif ($enum) { + if ($enum) { push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }"; + } elsif ($count == 1) { + push @lines_rb, "field(:$name, 0) { bit $shift }"; } else { push @lines_rb, "field(:$name, 0) { bits $shift, $count }"; } diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index a0438670d..c99d5b88d 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -107,6 +107,31 @@ module DFHack 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) @@ -162,5 +187,28 @@ module DFHack def inspect "#" 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.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 end end diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index a1cba4168..3507508e1 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -7,6 +7,7 @@ module DFHack def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end def _cpp_init ; end + def _cpp_delete ; end end class Compound < MemStruct @@ -34,8 +35,8 @@ module DFHack def float Float.new end - def bit(shift) - BitField.new(shift, 1) + def bit(shift, enum=nil) + BitField.new(shift, 1, enum) end def bits(shift, len, enum=nil) BitField.new(shift, len, enum) @@ -114,6 +115,11 @@ module DFHack def _cpp_init _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } end + def _cpp_delete + _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_delete } + DFHack.free(@_memaddr) + @_memaddr = nil # turn future segfaults in harmless ruby exceptions + end def _set(h) case h when Hash; h.each { |k, v| send("#{k}=", v) } @@ -147,7 +153,7 @@ module DFHack out << '>' end def inspect_field(n, o, s) - if s.kind_of?(BitField) and s._len == 1 + if s.kind_of?(BitField) and s._len == 1 and not s._enum send(n) ? n.to_s : '' elsif s.kind_of?(Pointer) "#{n}=#{s._at(@_memaddr+o).inspect}" @@ -242,7 +248,7 @@ module DFHack def _get v = DFHack.memory_read_int32(@_memaddr) >> @_shift - if @_len == 1 + if @_len == 1 and not @_enum ((v & 1) == 0) ? false : true else v &= _mask @@ -252,7 +258,7 @@ module DFHack end def _set(v) - if @_len == 1 + if @_len == 1 and (not @_enum or v == false or v == true) # allow 'bit = 0' v = (v && v != 0 ? 1 : 0) end @@ -355,6 +361,7 @@ module DFHack def empty? ; length == 0 ; end def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end def index(e=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == e } ; end + def map! ; (0...length).each { |i| self[i] = yield(self[i]) } ; end def first ; self[0] ; end def last ; self[length-1] ; end end @@ -372,6 +379,9 @@ module DFHack def _cpp_init _length.times { |i| _tgat(i)._cpp_init } end + def _cpp_delete + _length.times { |i| _tgat(i)._cpp_delete } + end alias length _length alias size _length def _tgat(i) @@ -423,10 +433,10 @@ module DFHack DFHack.memory_vector32_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector32_insert(@_memaddr, idx, val) + DFHack.memory_vector32_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector32_delete(@_memaddr, idx) + DFHack.memory_vector32_deleteat(@_memaddr, idx) end def _set(v) @@ -434,6 +444,12 @@ module DFHack v.each_with_index { |e, i| self[i] = e } # patch entries end + def self._cpp_new + new._at DFHack.memory_vector_new + end + def _cpp_delete + DFHack.memory_vector_delete(@_memaddr) + end def _cpp_init DFHack.memory_vector_init(@_memaddr) end @@ -496,10 +512,10 @@ module DFHack DFHack.memory_vector16_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector16_insert(@_memaddr, idx, val) + DFHack.memory_vector16_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector16_delete(@_memaddr, idx) + DFHack.memory_vector16_deleteat(@_memaddr, idx) end end class StlVector8 < StlVector32 @@ -510,10 +526,10 @@ module DFHack DFHack.memory_vector8_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector8_insert(@_memaddr, idx, val) + DFHack.memory_vector8_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector8_delete(@_memaddr, idx) + DFHack.memory_vector8_deleteat(@_memaddr, idx) end end class StlBitVector < StlVector32 @@ -522,10 +538,10 @@ module DFHack DFHack.memory_vectorbool_length(@_memaddr) end def insert_at(idx, val) - DFHack.memory_vectorbool_insert(@_memaddr, idx, val) + DFHack.memory_vectorbool_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vectorbool_delete(@_memaddr, idx) + DFHack.memory_vectorbool_deleteat(@_memaddr, idx) end def [](idx) idx += length if idx < 0 @@ -541,6 +557,12 @@ module DFHack DFHack.memory_vectorbool_setat(@_memaddr, idx, v) end end + def self._cpp_new + new._at DFHack.memory_vectorbool_new + end + def _cpp_delete + DFHack.memory_vectorbool_delete(@_memaddr) + end end class StlString < MemStruct def _get @@ -551,6 +573,12 @@ module DFHack DFHack.memory_write_stlstring(@_memaddr, v) end + def self._cpp_new + new._at DFHack.memory_stlstring_new + end + def _cpp_delete + DFHack.memory_stlstring_delete(@_memaddr) + end def _cpp_init DFHack.memory_stlstring_init(@_memaddr) end @@ -574,7 +602,7 @@ module DFHack def length DFHack.memory_bitarray_length(@_memaddr) end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def size ; length ; end def resize(len) DFHack.memory_bitarray_resize(@_memaddr, len) @@ -608,7 +636,7 @@ module DFHack def length ; _length ; end def size ; _length ; end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def _tgat(i) @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length end @@ -702,6 +730,21 @@ module DFHack def self.sym(v) ; (!v || (v == 0)) ? false : true ; end end + class StlString < MemHack::Compound + field(:str, 0) { stl_string } + + def self.cpp_new(init=nil) + s = MemHack::StlString._cpp_new + s._set(init) if init + new._at(s._memaddr) + end + + def _cpp_delete + MemHack::StlString.new._at(@_memaddr+0)._cpp_delete + @_memaddr = nil + end + end + # cpp rtti name -> rb class @rtti_n2c = {} @rtti_c2n = {} diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 08ea13b9f..1391faa44 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -528,7 +528,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len) if (!ptr) return Qnil; memset(ptr, 0, FIX2INT(len)); - return rb_uint2inum((long)ptr); + return rb_uint2inum((uint32_t)ptr); } static VALUE rb_dffree(VALUE self, VALUE ptr) @@ -599,6 +599,18 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) // stl::string +static VALUE rb_dfmemory_stlstring_new(VALUE self) +{ + std::string *ptr = new std::string; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_stlstring_delete(VALUE self, VALUE addr) +{ + std::string *ptr = (std::string*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr) { // XXX THIS IS TERRIBLE @@ -621,6 +633,18 @@ static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) // vector access +static VALUE rb_dfmemory_vec_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr) { std::vector *ptr = new std::vector; @@ -638,13 +662,13 @@ static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec8_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec8_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec8_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -662,13 +686,13 @@ static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec16_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec16_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec16_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -686,13 +710,13 @@ static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec32_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec32_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -700,6 +724,24 @@ static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) } // vector +static VALUE rb_dfmemory_vecbool_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} +static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr) +{ + std::vector *ptr = new std::vector; + memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr)); + return Qtrue; +} static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr) { std::vector *v = (std::vector*)rb_num2ulong(addr); @@ -716,13 +758,13 @@ static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } -static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vecbool_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1)); return Qtrue; } -static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vecbool_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -834,27 +876,34 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_new", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_delete", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector_new", RUBY_METHOD_FUNC(rb_dfmemory_vec_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vector_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_deleteat), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_new", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_init", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2); diff --git a/plugins/ruby/ui.rb b/plugins/ruby/ui.rb index 6d2b5c2cd..9dded66c2 100644 --- a/plugins/ruby/ui.rb +++ b/plugins/ruby/ui.rb @@ -68,5 +68,14 @@ module DFHack world.status.display_timer = 2000 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 end diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 10d7b52c2..0b3fa8a99 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -12,6 +12,7 @@ #include "df/world.h" #include "df/job.h" #include "df/job_item.h" +#include "df/job_item_ref.h" #include "df/general_ref.h" #include "df/builtin_mats.h" #include "df/inorganic_raw.h" @@ -165,7 +166,10 @@ command_result df_showmood (color_ostream &out, vector & parameters) out.print("not yet claimed a workshop but will want"); out.print(" the following items:\n"); - int count_got = job->items.size(), got; + // total amount of stuff fetched so far + int count_got = 0; + for (size_t i = 0; i < job->items.size(); i++) + count_got += job->items[i]->item->getTotalDimension(); for (size_t i = 0; i < job->job_items.size(); i++) { @@ -269,7 +273,9 @@ command_result df_showmood (color_ostream &out, vector & parameters) } } - got = count_got; + // total amount of stuff fetched for this requirement + // XXX may fail with cloth/thread/bars if need 1 and fetch 2 + int got = count_got; if (got > item->quantity) got = item->quantity; out.print(", quantity %i (got %i)\n", item->quantity, got); diff --git a/scripts/fix/loyaltycascade.rb b/scripts/fix/loyaltycascade.rb new file mode 100644 index 000000000..6fad2947f --- /dev/null +++ b/scripts/fix/loyaltycascade.rb @@ -0,0 +1,64 @@ +# script to fix loyalty cascade, when you order your militia to kill friendly units + +def fixunit(unit) + return if unit.race != df.ui.race_id or unit.civ_id != df.ui.civ_id + links = unit.hist_figure_tg.entity_links + fixed = false + + # check if the unit is a civ renegade + if i1 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and + l.entity_id == df.ui.civ_id + } and i2 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and + l.entity_id == df.ui.civ_id + } + fixed = true + i1, i2 = i2, i1 if i1 > i2 + links.delete_at i2 + links.delete_at i1 + links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.civ_id, :link_strength => 100) + df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.civ_tg.name} again" + end + + # check if the unit is a group renegade + if i1 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and + l.entity_id == df.ui.group_id + } and i2 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and + l.entity_id == df.ui.group_id + } + fixed = true + i1, i2 = i2, i1 if i1 > i2 + links.delete_at i2 + links.delete_at i1 + links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.group_id, :link_strength => 100) + df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.group_tg.name} again" + end + + # fix the 'is an enemy' cache matrix (mark to be recalculated by the game when needed) + if fixed and unit.unknown8.enemy_status_slot != -1 + i = unit.unknown8.enemy_status_slot + unit.unknown8.enemy_status_slot = -1 + cache = df.world.enemy_status_cache + cache.slot_used[i] = false + cache.rel_map[i].map! { -1 } + cache.rel_map.each { |a| a[i] = -1 } + cache.next_slot = i if cache.next_slot > i + end + + # return true if we actually fixed the unit + fixed +end + +count = 0 +df.unit_citizens.each { |u| + count += 1 if fixunit(u) +} + +if count > 0 + puts "loyalty cascade fixed (#{count} dwarves)" +else + puts "no loyalty cascade found" +end diff --git a/scripts/fixstuckdoors.rb b/scripts/fix/stuckdoors.rb similarity index 100% rename from scripts/fixstuckdoors.rb rename to scripts/fix/stuckdoors.rb diff --git a/scripts/magmasource.rb b/scripts/magmasource.rb index 8525d51e0..e97080834 100644 --- a/scripts/magmasource.rb +++ b/scripts/magmasource.rb @@ -12,26 +12,16 @@ when 'here' end $magma_sources.each { |x, y, z| - if tile = df.map_tile_at(x, y, z) and DFHack::TiletypeShape::PassableFlow[tile.shape] + if tile = df.map_tile_at(x, y, z) and tile.shape_passableflow des = tile.designation - des.flow_size += 1 if des.flow_size < 7 - des.liquid_type = 1 - des.flow_forbid = true - - mf = tile.mapblock.flags - mf.update_liquid = true - mf.update_liquid_twice = true - - zf = df.world.map.z_level_flags[z] - zf.update = true - zf.update_twice = true + tile.spawn_magma(des.flow_size + 1) if des.flow_size < 7 end } } if df.cursor.x != -30000 if tile = df.map_tile_at(df.cursor) - if DFHack::TiletypeShape::PassableFlow[tile.shape] + if tile.shape_passableflow $magma_sources << [df.cursor.x, df.cursor.y, df.cursor.z] else puts "Impassable tile: I'm afraid I can't do that, Dave" diff --git a/scripts/slayrace.rb b/scripts/slayrace.rb index 27b1ba3cc..b1fc24e35 100644 --- a/scripts/slayrace.rb +++ b/scripts/slayrace.rb @@ -1,6 +1,9 @@ # slay all creatures of a given race +# race = name of the race to eradicate, use 'him' to target only the selected creature race = $script_args[0] +# if the 2nd parameter is 'magma', magma rain for the targets instead of instant death +magma = ($script_args[1] == 'magma') checkunit = lambda { |u| u.body.blood_count != 0 and @@ -9,12 +12,39 @@ checkunit = lambda { |u| not df.map_designation_at(u).hidden } +slayit = lambda { |u| + if not magma + # just make them drop dead + u.body.blood_count = 0 + # some races dont mind having no blood, ensure they are still taken care of. + u.animal.vanish_countdown = 2 + else + # it's getting hot around here + # !!WARNING!! do not call on a magma-safe creature + ouh = df.onupdate_register(1) { + if u.flags1.dead + df.onupdate_unregister(ouh) + else + x, y, z = u.pos.x, u.pos.y, u.pos.z + z += 1 while tile = df.map_tile_at(x, y, z+1) and tile.shape_passableflow + df.map_tile_at(x, y, z).spawn_magma(7) + end + } + end +} + all_races = df.world.units.active.map { |u| u.race_tg.creature_id if checkunit[u] }.compact.uniq.sort if !race puts all_races +elsif race == 'him' + if him = df.unit_find + slayit[him] + else + puts "Choose target" + end else raw_race = df.match_rawname(race, all_races) raise 'invalid race' if not raw_race @@ -24,7 +54,7 @@ else count = 0 df.world.units.active.each { |u| if u.race == race_nr and checkunit[u] - u.body.blood_count = 0 + slayit[u] count += 1 end }