Merge branch 'master' of git://github.com/jjyg/dfhack

develop
Warmist 2012-12-12 18:58:11 +02:00
commit ddceabbfb7
13 changed files with 308 additions and 35 deletions

@ -24,6 +24,7 @@ DFHack future
- lever: list and pull fort levers from the dfhack console. - lever: list and pull fort levers from the dfhack console.
- stripcaged: mark items inside cages for dumping, eg caged goblin weapons. - stripcaged: mark items inside cages for dumping, eg caged goblin weapons.
- soundsense-season: writes the correct season to gamelog.txt on world load. - soundsense-season: writes the correct season to gamelog.txt on world load.
- devel/create-items: spawn items
New GUI scripts: New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders. - gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them.

@ -316,7 +316,7 @@ static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr,
rbcmd += "'" + args[i] + "', "; rbcmd += "'" + args[i] + "', ";
rbcmd += "]\n"; rbcmd += "]\n";
rbcmd += "load './hack/scripts/" + name + ".rb'"; rbcmd += "catch(:script_finished) { load './hack/scripts/" + name + ".rb' }";
return plug_mgr->eval_ruby(out, rbcmd.c_str()); return plug_mgr->eval_ruby(out, rbcmd.c_str());
} }

@ -785,10 +785,10 @@ bool DFHack::Units::isSane(df::unit *unit)
if (unit->flags1.bits.dead || if (unit->flags1.bits.dead ||
unit->flags3.bits.ghostly || unit->flags3.bits.ghostly ||
isOpposedToLife(unit) || isOpposedToLife(unit) ||
unit->unknown8.unk2) unit->enemy.undead)
return false; return false;
if (unit->unknown8.normal_race == unit->unknown8.were_race && isCrazed(unit)) if (unit->enemy.normal_race == unit->enemy.were_race && isCrazed(unit))
return false; return false;
switch (unit->mood) switch (unit->mood)
@ -839,7 +839,7 @@ bool DFHack::Units::isDwarf(df::unit *unit)
CHECK_NULL_POINTER(unit); CHECK_NULL_POINTER(unit);
return unit->race == ui->race_id || return unit->race == ui->race_id ||
unit->unknown8.normal_race == ui->race_id; unit->enemy.normal_race == ui->race_id;
} }
double DFHack::Units::getAge(df::unit *unit, bool true_age) double DFHack::Units::getAge(df::unit *unit, bool true_age)
@ -1225,12 +1225,12 @@ int Units::computeMovementSpeed(df::unit *unit)
// Stance // Stance
if (!unit->flags1.bits.on_ground && unit->status2.able_stand > 2) if (!unit->flags1.bits.on_ground && unit->status2.limbs_stand_max > 2)
{ {
// WTF // WTF
int as = unit->status2.able_stand; int as = unit->status2.limbs_stand_max;
int x = (as-1) - (as>>1); int x = (as-1) - (as>>1);
int y = as - unit->status2.able_stand_impair; int y = as - unit->status2.limbs_stand_count;
if (unit->flags3.bits.on_crutch) y--; if (unit->flags3.bits.on_crutch) y--;
y = y * 500 / x; y = y * 500 / x;
if (y > 0) speed += y; if (y > 0) speed += y;

@ -189,10 +189,10 @@ function healunit(unit)
unit.body.wounds:resize(0) -- memory leak here :/ unit.body.wounds:resize(0) -- memory leak here :/
unit.body.blood_count=unit.body.blood_max unit.body.blood_count=unit.body.blood_max
--set flags for standing and grasping... --set flags for standing and grasping...
unit.status2.able_stand=4 unit.status2.limbs_stand_max=4
unit.status2.able_stand_impair=4 unit.status2.limbs_stand_count=4
unit.status2.able_grasp=4 unit.status2.limbs_grasp_max=4
unit.status2.able_grasp_impair=4 unit.status2.limbs_grasp_count=4
--should also set temperatures, and flags for breath etc... --should also set temperatures, and flags for breath etc...
unit.flags1.dead=false unit.flags1.dead=false
unit.flags2.calculated_bodyparts=false unit.flags2.calculated_bodyparts=false
@ -240,4 +240,4 @@ function powerup(unit,labor_rating,military_rating,skills)
end end
menu:add("Power up",powerup) menu:add("Power up",powerup)
return _ENV return _ENV

@ -48,6 +48,7 @@ If you create such a script, e.g. 'test.rb', that will add a new dfhack console
command 'test'. command 'test'.
The script can access the console command arguments through the global variable The script can access the console command arguments through the global variable
'$script_args', which is an array of ruby Strings. '$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 The help string displayed in dfhack 'ls' command is the first line of the
script, if it is a comment (ie starts with '# '). script, if it is a comment (ie starts with '# ').

@ -39,7 +39,7 @@ static color_ostream *r_console; // color_ostream given as argument, if NU
static const char *r_command; static const char *r_command;
static tthread::thread *r_thread; static tthread::thread *r_thread;
static int onupdate_active; static int onupdate_active;
static int onupdate_minyear, onupdate_minyeartick; static int onupdate_minyear, onupdate_minyeartick=-1, onupdate_minyeartickadv=-1;
static color_ostream_proxy *console_proxy; static color_ostream_proxy *console_proxy;
@ -180,10 +180,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (!onupdate_active) if (!onupdate_active)
return CR_OK; return CR_OK;
if (*df::global::cur_year < onupdate_minyear) if (df::global::cur_year && (*df::global::cur_year < onupdate_minyear))
return CR_OK; return CR_OK;
if (*df::global::cur_year == onupdate_minyear && if (df::global::cur_year_tick && onupdate_minyeartick >= 0 &&
*df::global::cur_year_tick < onupdate_minyeartick) (*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick < onupdate_minyeartick))
return CR_OK;
if (df::global::cur_year_tick_advmode && onupdate_minyeartickadv >= 0 &&
(*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick_advmode < onupdate_minyeartickadv))
return CR_OK; return CR_OK;
return plugin_eval_ruby(out, "DFHack.onupdate"); return plugin_eval_ruby(out, "DFHack.onupdate");
@ -481,6 +486,17 @@ static VALUE rb_dfonupdate_minyeartick_set(VALUE self, VALUE val)
return Qtrue; return Qtrue;
} }
static VALUE rb_dfonupdate_minyeartickadv(VALUE self)
{
return rb_uint2inum(onupdate_minyeartickadv);
}
static VALUE rb_dfonupdate_minyeartickadv_set(VALUE self, VALUE val)
{
onupdate_minyeartickadv = rb_num2ulong(val);
return Qtrue;
}
static VALUE rb_dfprint_str(VALUE self, VALUE s) static VALUE rb_dfprint_str(VALUE self, VALUE s)
{ {
if (r_console) if (r_console)
@ -531,6 +547,23 @@ static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr)
return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr)); return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr));
} }
// run a dfhack command, as if typed from the dfhack console
static VALUE rb_dfhack_run(VALUE self, VALUE cmd)
{
if (!r_console) // XXX
return Qnil;
std::string s;
int strlen = FIX2INT(rb_funcall(cmd, rb_intern("length"), 0));
s.assign(rb_string_value_ptr(&cmd), strlen);
// allow the target command to suspend
// FIXME
//CoreSuspendClaimer suspend(true);
Core::getInstance().runCommand(*r_console, s);
return Qtrue;
}
@ -663,6 +696,49 @@ static VALUE rb_dfmemory_patch(VALUE self, VALUE addr, VALUE raw)
return ret ? Qtrue : Qfalse; return ret ? Qtrue : Qfalse;
} }
// allocate memory pages
static VALUE rb_dfmemory_pagealloc(VALUE self, VALUE len)
{
void *ret = Core::getInstance().p->memAlloc(rb_num2ulong(len));
return (ret == (void*)-1) ? Qnil : rb_uint2inum((uint32_t)ret);
}
// free memory from pagealloc
static VALUE rb_dfmemory_pagedealloc(VALUE self, VALUE ptr, VALUE len)
{
int ret = Core::getInstance().p->memDealloc((void*)rb_num2ulong(ptr), rb_num2ulong(len));
return ret ? Qfalse : Qtrue;
}
// change memory page permissions
// ptr must be page-aligned
// prot is a String, eg 'rwx', 'r', 'x'
static VALUE rb_dfmemory_pageprotect(VALUE self, VALUE ptr, VALUE len, VALUE prot_str)
{
int ret, prot=0;
char *prot_p = rb_string_value_ptr(&prot_str);
if (*prot_p == 'r') {
prot |= Process::MemProt::READ;
++prot_p;
}
if (*prot_p == 'w') {
prot |= Process::MemProt::WRITE;
++prot_p;
}
if (*prot_p == 'x') {
prot |= Process::MemProt::EXEC;
++prot_p;
}
Core::printerr("pageprot %x %x %x\n", rb_num2ulong(ptr), rb_num2ulong(len), prot);
ret = Core::getInstance().p->memProtect((void*)rb_num2ulong(ptr), rb_num2ulong(len), prot);
return ret ? Qfalse : Qtrue;
}
// stl::string // stl::string
static VALUE rb_dfmemory_stlstring_new(VALUE self) static VALUE rb_dfmemory_stlstring_new(VALUE self)
@ -963,14 +1039,20 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1); rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0); rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1); rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv_set), 1);
rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1);
rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1);
//rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1);
rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1);
rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1);
rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);
rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1);
rb_define_singleton_method(rb_cDFHack, "pagealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagealloc), 1);
rb_define_singleton_method(rb_cDFHack, "pagedealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagedealloc), 2);
rb_define_singleton_method(rb_cDFHack, "pageprotect", RUBY_METHOD_FUNC(rb_dfmemory_pageprotect), 3);
rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 8); rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 8);
rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfversion), 0); rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfversion), 0);

@ -64,6 +64,7 @@ module DFHack
# register a callback to be called every gframe or more # register a callback to be called every gframe or more
# ex: DFHack.onupdate_register('fastdwarf') { DFHack.world.units[0].counters.job_counter = 0 } # 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) def onupdate_register(descr, ticklimit=nil, initialtickdelay=0, &b)
raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String) raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String)
@onupdate_list ||= [] @onupdate_list ||= []
@ -82,7 +83,7 @@ module DFHack
@onupdate_list.delete b @onupdate_list.delete b
if @onupdate_list.empty? if @onupdate_list.empty?
DFHack.onupdate_active = false DFHack.onupdate_active = false
DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = 0 DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = DFHack.onupdate_minyeartickadv = -1
end end
end end
@ -94,20 +95,32 @@ module DFHack
end end
TICKS_PER_YEAR = 1200*28*12 TICKS_PER_YEAR = 1200*28*12
# this method is called by dfhack every 'onupdate' if onupdate_active is true # this method is called by ruby.cpp if df.onupdate_active is true
def onupdate def onupdate
@onupdate_list ||= [] @onupdate_list ||= []
ticks_per_year = TICKS_PER_YEAR y = cur_year
ticks_per_year *= 72 if gametype == :ADVENTURE_MAIN or gametype == :ADVENTURE_ARENA 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
end
@onupdate_list.each { |o| @onupdate_list.each { |o|
o.check_run(cur_year, cur_year_tick, ticks_per_year) o.check_run(y, yt, ytmax)
} }
if onext = @onupdate_list.sort.first if onext = @onupdate_list.sort.first
DFHack.onupdate_minyear = onext.minyear DFHack.onupdate_minyear = onext.minyear
DFHack.onupdate_minyeartick = onext.minyeartick 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
end end

@ -76,7 +76,7 @@ module DFHack
u.mood == :Berserk or u.mood == :Berserk or
unit_testflagcurse(u, :CRAZED) or unit_testflagcurse(u, :CRAZED) or
unit_testflagcurse(u, :OPPOSED_TO_LIFE) or unit_testflagcurse(u, :OPPOSED_TO_LIFE) or
u.unknown8.unk2 or u.enemy.undead or
u.flags3.ghostly or u.flags3.ghostly or
u.flags1.marauder or u.flags1.active_invader or u.flags1.invader_origin or u.flags1.marauder or u.flags1.active_invader or u.flags1.invader_origin or
u.flags1.forest or u.flags1.forest or
@ -113,6 +113,9 @@ module DFHack
true true
end 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 }
# list workers (citizen, not crazy / child / inmood / noble) # list workers (citizen, not crazy / child / inmood / noble)
def unit_workers def unit_workers
world.units.active.find_all { |u| world.units.active.find_all { |u|

@ -694,7 +694,7 @@ static int adjust_unit_divisor(int value) {
static bool can_spar(df::unit *unit) { static bool can_spar(df::unit *unit) {
return unit->counters2.exhaustion <= 2000 && // actually 4000, but leave a gap return unit->counters2.exhaustion <= 2000 && // actually 4000, but leave a gap
(unit->status2.able_grasp_impair > 0 || unit->status2.able_grasp == 0) && (unit->status2.limbs_grasp_count > 0 || unit->status2.limbs_grasp_max == 0) &&
(!unit->health || (unit->health->flags.whole&0x7FF) == 0) && (!unit->health || (unit->health->flags.whole&0x7FF) == 0) &&
(!unit->job.current_job || unit->job.current_job != job_type::Rest); (!unit->job.current_job || unit->job.current_job != job_type::Rest);
} }

@ -10,6 +10,20 @@ def display_death_event(e)
puts str.chomp(',') + '.' puts str.chomp(',') + '.'
end end
def display_death_unit(u)
death_info = u.counters.death_tg
killer = death_info.killer_tg if death_info
str = "The #{u.race_tg.name[0]}"
str << " #{u.name}" if u.name.has_name
str << " died"
str << " in year #{death_info.event_year}" if death_info
str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1
str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer
puts str.chomp(',') + '.'
end
item = df.item_find(:selected) item = df.item_find(:selected)
unit = df.unit_find(:selected) unit = df.unit_find(:selected)
@ -27,8 +41,11 @@ if not hf
puts "Please select a corpse in the loo'k' menu, or an unit in the 'u'nitlist screen" puts "Please select a corpse in the loo'k' menu, or an unit in the 'u'nitlist screen"
elsif hf == -1 elsif hf == -1
# TODO try to retrieve info from the unit (u = item.unit_tg) if unit ||= item.unit_tg
puts "Not a historical figure, cannot death find info" display_death_unit(unit)
else
puts "Not a historical figure, cannot death find info"
end
else else
histfig = df.world.history.figures.binsearch(hf) histfig = df.world.history.figures.binsearch(hf)

@ -0,0 +1,161 @@
# create arbitrary items under cursor
category = $script_args[0] || 'help'
mat_raw = $script_args[1] || 'list'
count = $script_args[2]
category = df.match_rawname(category, ['help', 'bars', 'boulders', 'plants', 'logs', 'webs']) || 'help'
if category == 'help'
puts <<EOS
Create items under the cursor.
Usage:
create [category] [raws token] [number]
Item categories:
bars, boulders, plants, logs, web
Raw token:
either a full token (PLANT_MAT:ADLER:WOOD) or the middle part only
(the missing part is autocompleted depending on the item category)
use 'list' to show all possibilities
Exemples:
create boulders hematite 30
create bars CREATURE_MAT:CAT:SOAP 10
create web cave_giant
create plants list
EOS
throw :script_finished
elsif mat_raw == 'list'
# allowed with no cursor
elsif df.cursor.x == -30000
puts "Please place the game cursor somewhere"
throw :script_finished
elsif !(maptile = df.map_tile_at(df.cursor))
puts "Error: unallocated map block !"
throw :script_finished
elsif !maptile.shape_passablehigh
puts "Error: impassible tile !"
throw :script_finished
end
def match_list(tok, list)
if tok != 'list'
tok = df.match_rawname(tok, list)
if not tok
puts "Invalid raws token, use one in:"
tok = 'list'
end
end
if tok == 'list'
puts list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.join(' ')
throw :script_finished
end
tok
end
case category
when 'bars'
# create metal bar, eg createbar INORGANIC:IRON
cls = DFHack::ItemBarst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.inorganics.find_all { |ino|
ino.material.flags[:IS_METAL]
}.map { |ino| ino.id }
mat_raw = match_list(mat_raw, list)
mat_raw = "INORGANIC:#{mat_raw}"
puts mat_raw
end
customize = lambda { |item|
item.dimension = 150
item.subtype = -1
}
when 'boulders'
cls = DFHack::ItemBoulderst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.inorganics.find_all { |ino|
ino.material.flags[:IS_STONE]
}.map { |ino| ino.id }
mat_raw = match_list(mat_raw, list)
mat_raw = "INORGANIC:#{mat_raw}"
puts mat_raw
end
when 'plants'
cls = DFHack::ItemPlantst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.plants.all.find_all { |plt|
plt.material.find { |mat| mat.id == 'STRUCTURAL' }
}.map { |plt| plt.id }
mat_raw = match_list(mat_raw, list)
mat_raw = "PLANT_MAT:#{mat_raw}:STRUCTURAL"
puts mat_raw
end
when 'logs'
cls = DFHack::ItemWoodst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.plants.all.find_all { |plt|
plt.material.find { |mat| mat.id == 'WOOD' }
}.map { |plt| plt.id }
mat_raw = match_list(mat_raw, list)
mat_raw = "PLANT_MAT:#{mat_raw}:WOOD"
puts mat_raw
end
when 'webs'
cls = DFHack::ItemThreadst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.creatures.all.find_all { |cre|
cre.material.find { |mat| mat.id == 'SILK' }
}.map { |cre| cre.creature_id }
mat_raw = match_list(mat_raw, list)
mat_raw = "CREATURE_MAT:#{mat_raw}:SILK"
puts mat_raw
end
count ||= 1
customize = lambda { |item|
item.flags.spider_web = true
item.dimension = 15000 # XXX may depend on creature (this is for GCS)
}
end
mat = df.decode_mat mat_raw
count ||= 20
count.to_i.times {
item = cls.cpp_new
item.id = df.item_next_id
item.stack_size = 1
item.mat_type = mat.mat_type
item.mat_index = mat.mat_index
customize[item] if customize
df.item_next_id += 1
item.categorize(true)
df.world.items.all << item
item.pos = df.cursor
item.flags.on_ground = true
df.map_tile_at.mapblock.items << item.id
df.map_tile_at.occupancy.item = true
}
# move game view, so that the ui menu updates
df.curview.feed_keys(:CURSOR_UP_Z)
df.curview.feed_keys(:CURSOR_DOWN_Z)

@ -38,9 +38,9 @@ def fixunit(unit)
end end
# fix the 'is an enemy' cache matrix (mark to be recalculated by the game when needed) # 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 if fixed and unit.enemy.enemy_status_slot != -1
i = unit.unknown8.enemy_status_slot i = unit.enemy.enemy_status_slot
unit.unknown8.enemy_status_slot = -1 unit.enemy.enemy_status_slot = -1
cache = df.world.enemy_status_cache cache = df.world.enemy_status_cache
cache.slot_used[i] = false cache.slot_used[i] = false
cache.rel_map[i].map! { -1 } cache.rel_map[i].map! { -1 }

@ -8,12 +8,7 @@ when 'add'
if u = df.unit_find if u = df.unit_find
$superdwarf_ids |= [u.id] $superdwarf_ids |= [u.id]
if df.gamemode == :ADVENTURE and not df.respond_to?(:cur_year_tick_advmode) $superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) {
onupdate_delay = nil
else
onupdate_delay = 1
end
$superdwarf_onupdate ||= df.onupdate_register('superdwarf', onupdate_delay) {
if $superdwarf_ids.empty? if $superdwarf_ids.empty?
df.onupdate_unregister($superdwarf_onupdate) df.onupdate_unregister($superdwarf_onupdate)
$superdwarf_onupdate = nil $superdwarf_onupdate = nil