diff --git a/plugins/ruby/README b/plugins/ruby/README index 8a473f332..c9a84fb37 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -213,8 +213,7 @@ Find the raws name of the plant under cursor p df.world.raws.plants.all[plant.mat_index].id Dig a channel under the cursor - df.map_designation_at(df.cursor).dig = :Channel - df.map_block_at(df.cursor).flags.designated = true + 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' } diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index ab029ac24..af152e198 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -279,9 +279,9 @@ module DFHack job = Job.cpp_new refbuildingholder = GeneralRefBuildingHolderst.cpp_new job.job_type = :DestroyBuilding - refbuildingholder.building_id = building.id + refbuildingholder.building_id = bld.id job.references << refbuildingholder - building.jobs << job + bld.jobs << job job_link job job end diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index c99d5b88d..dccea7291 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -188,6 +188,11 @@ module DFHack "#" end + def dig(mode=:Default) + designation.dig = mode + mapblock.flags.designated = true + end + def spawn_liquid(quantity, is_magma=false, flowing=true) designation.flow_size = quantity designation.liquid_type = (is_magma ? :Magma : :Water) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 3507508e1..0cee6426f 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -666,9 +666,9 @@ module DFHack @_tg = tg end - field(:_ptr, 0) { number 32, false } - field(:_prev, 4) { number 32, false } - field(:_next, 8) { number 32, false } + field(:_ptr, 0) { pointer } + field(:_prev, 4) { pointer } + field(:_next, 8) { pointer } def item # With the current xml structure, currently _tg designate @@ -682,22 +682,24 @@ module DFHack def item=(v) #addr = _ptr - #raise 'null pointer' if addr == 0 + #raise 'null pointer' if not addr #@_tg.at(addr)._set(v) raise 'null pointer' end def prev addr = _prev - return if addr == 0 + return if not addr @_tg._at(addr)._get end def next addr = _next - return if addr == 0 + return if not addr @_tg._at(addr)._get end + alias next= _next= + alias prev= _prev= include Enumerable def each diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 1391faa44..482714d2a 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -4,6 +4,7 @@ #include "Export.h" #include "PluginManager.h" #include "VersionInfo.h" +#include "MemAccess.h" #include "DataDefs.h" #include "df/global_objects.h" @@ -597,6 +598,45 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) return Qtrue; } +// return memory permissions at address (eg "rx", nil if unmapped) +static VALUE rb_dfmemory_check(VALUE self, VALUE addr) +{ + void *ptr = (void*)rb_num2ulong(addr); + std::vector ranges; + Core::getInstance().p->getMemRanges(ranges); + + unsigned i = 0; + while (i < ranges.size() && ranges[i].end <= ptr) + i++; + + if (i >= ranges.size() || ranges[i].start > ptr || !ranges[i].valid) + return Qnil; + + std::string perm = ""; + if (ranges[i].read) + perm += "r"; + if (ranges[i].write) + perm += "w"; + if (ranges[i].execute) + perm += "x"; + if (ranges[i].shared) + perm += "s"; + + return rb_str_new(perm.c_str(), perm.length()); +} + +// memory write (tmp override page permissions, eg patch code) +static VALUE rb_dfmemory_patch(VALUE self, VALUE addr, VALUE raw) +{ + int strlen = FIX2INT(rb_funcall(raw, rb_intern("length"), 0)); + bool ret; + + ret = Core::getInstance().p->patchMemory((void*)rb_num2ulong(addr), + rb_string_value_ptr(&raw), strlen); + + return ret ? Qtrue : Qfalse; +} + // stl::string static VALUE rb_dfmemory_stlstring_new(VALUE self) @@ -875,6 +915,8 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_write_int16", RUBY_METHOD_FUNC(rb_dfmemory_write_int16), 2); 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_check", RUBY_METHOD_FUNC(rb_dfmemory_check), 1); + rb_define_singleton_method(rb_cDFHack, "memory_patch", RUBY_METHOD_FUNC(rb_dfmemory_patch), 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); diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index ebcf249da..1a619c5ce 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -41,48 +41,60 @@ module DFHack # returns an Array of all units that are current fort citizen (dwarves, on map, not hostile) def unit_citizens - race = ui.race_id - civ = ui.civ_id world.units.active.find_all { |u| - u.race == race and u.civ_id == civ and !u.flags1.dead and !u.flags1.merchant and - !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and - !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and - u.mood != :Berserk - # TODO check curse ; currently this should keep vampires, but may include werebeasts + unit_iscitizen(u) } end + def unit_iscitizen(u) + u.race == ui.race_id and u.civ_id == ui.civ_id and !u.flags1.dead and !u.flags1.merchant and + !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and + !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and + u.mood != :Berserk + # TODO check curse ; currently this should keep vampires, but may include werebeasts + end + # list workers (citizen, not crazy / child / inmood / noble) def unit_workers - unit_citizens.find_all { |u| - 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] } + world.units.active.find_all { |u| + unit_isworker(u) } end + def unit_isworker(u) + unit_iscitizen(u) 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 - unit_workers.find_all { |u| - # 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_index == -1 and - # filter 'on break' - not u.status.misc_traits.find { |t| t.id == :OnBreak } + 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_index == -1 and + # filter 'on break' + not u.status.misc_traits.find { |t| t.id == :OnBreak } + end + def unit_entitypositions(unit) list = [] - return list if not hf = world.history.figures.binsearch(unit.hist_figure_id) + return list if not hf = unit.hist_figure_tg hf.entity_links.each { |el| next if el._rtti_classname != :histfig_entity_link_positionst - next if not ent = world.entities.all.binsearch(el.entity_id) + 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