Petr Mrázek 2012-08-12 01:10:37 +02:00
commit 7cc100b26e
16 changed files with 380 additions and 94 deletions

6
.gitignore vendored

@ -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

@ -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

@ -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);

@ -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")

@ -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 <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
@ -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,
<ld:global-type ld:meta="struct-type" type-name="unit">
@ -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,

@ -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

@ -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 }";
}

@ -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
"#<MapTile pos=[#@x, #@y, #@z] shape=#{shape} tilemat=#{tilemat} material=#{mat_info.token}>"
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

@ -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 = {}

@ -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<uint8_t> *ptr = new std::vector<uint8_t>;
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr)
{
std::vector<uint8_t> *ptr = (std::vector<uint8_t>*)rb_num2ulong(addr);
if (ptr)
delete ptr;
return Qtrue;
}
static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr)
{
std::vector<uint8_t> *ptr = new std::vector<uint8_t>;
@ -638,13 +662,13 @@ static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx)
std::vector<uint8_t> *v = (std::vector<uint8_t>*)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<uint8_t> *v = (std::vector<uint8_t>*)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<uint8_t> *v = (std::vector<uint8_t>*)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<uint16_t> *v = (std::vector<uint16_t>*)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<uint16_t> *v = (std::vector<uint16_t>*)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<uint16_t> *v = (std::vector<uint16_t>*)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<uint32_t> *v = (std::vector<uint32_t>*)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<uint32_t> *v = (std::vector<uint32_t>*)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<uint32_t> *v = (std::vector<uint32_t>*)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<bool>
static VALUE rb_dfmemory_vecbool_new(VALUE self)
{
std::vector<bool> *ptr = new std::vector<bool>;
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr)
{
std::vector<bool> *ptr = (std::vector<bool>*)rb_num2ulong(addr);
if (ptr)
delete ptr;
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr)
{
std::vector<bool> *ptr = new std::vector<bool>;
memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr));
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr)
{
std::vector<bool> *v = (std::vector<bool>*)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<bool> *v = (std::vector<bool>*)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<bool> *v = (std::vector<bool>*)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);

@ -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

@ -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 <string> & 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 <string> & 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);

@ -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

@ -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"

@ -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
}