From efdb709284d393938db6b60185ee3898cb068954 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 00:19:29 +0400 Subject: [PATCH 1/5] Support creating rollers and stops with dfhack.buildings.constructBuilding. --- library/lua/dfhack/buildings.lua | 18 ++++++++++++++++-- library/modules/Buildings.cpp | 17 ++++++++++++++++- library/xml | 2 +- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index ad82da89a..af9ad2605 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -178,7 +178,20 @@ local building_inputs = { }, [df.building_type.Slab] = { { item_type=df.item_type.SLAB } }, [df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } }, - [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } } + [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } }, + [df.building_type.Rollers] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + quantity=-1, + vector_id=df.job_item_vector_id.TRAPPARTS + }, + { + name='chain', + item_type=df.item_type.CHAIN, + vector_id=df.job_item_vector_id.CHAIN + } + } } --[[ Furnace building input material table. ]] @@ -318,7 +331,8 @@ local trap_inputs = { item_type=df.item_type.TRAPPARTS, vector_id=df.job_item_vector_id.TRAPPARTS } - } + }, + [df.trap_type.TrackStop] = { { flags2={ building_material=true, non_economic=true } } } } --[[ Functions for lookup in tables. ]] diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 65cf009f3..d2b3e3527 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -66,6 +66,7 @@ using namespace DFHack; #include "df/building_screw_pumpst.h" #include "df/building_water_wheelst.h" #include "df/building_wellst.h" +#include "df/building_rollersst.h" using namespace df::enums; using df::global::ui; @@ -289,6 +290,10 @@ bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d ¢er, makeOneDim(size, center, direction); return true; + case Rollers: + makeOneDim(size, center, (direction&1) == 0); + return true; + case WaterWheel: size = df::coord2d(3,3); makeOneDim(size, center, direction); @@ -592,6 +597,12 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) obj->direction = (df::screw_pump_direction)direction; break; } + case Rollers: + { + auto obj = (df::building_rollersst*)bld; + obj->direction = (df::screw_pump_direction)direction; + break; + } case Bridge: { auto obj = (df::building_bridgest*)bld; @@ -881,7 +892,11 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorquantity < 0) items[i]->quantity = computeMaterialAmount(bld); - job->job_items.push_back(items[i]); + /* The game picks up explicitly listed items in reverse + * order, but processes filters straight. This reverses + * the order of filters so as to produce the same final + * contained_items ordering as the normal ui way. */ + vector_insert_at(job->job_items, 0, items[i]); if (items[i]->item_type == item_type::BOULDER) rough = true; diff --git a/library/xml b/library/xml index 92dbeb626..b0d8e71dc 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 92dbeb6267a24a35babe3d5326801f2e8cfd22e1 +Subproject commit b0d8e71dc9f6e697409bd999801097acdc57fcd8 From 2c0024adc91491b6d687f3b56d6a5779a3391e59 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 19:56:55 +0400 Subject: [PATCH 2/5] Make Items::getPosition exactly match the DF original in behavior. --- LUA_API.rst | 4 ++-- Lua API.html | 4 ++-- library/modules/Items.cpp | 34 +++++++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index bd2d33cb0..9ecd2e55e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -693,7 +693,7 @@ Units module * ``dfhack.units.getPosition(unit)`` - Returns true *x,y,z* of the unit; may be not equal to unit.pos if caged. + Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. * ``dfhack.units.getContainer(unit)`` @@ -752,7 +752,7 @@ Items module * ``dfhack.items.getPosition(item)`` - Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory. + Returns true *x,y,z* of the item, or *nil* if invalid; may be not equal to item.pos if in inventory. * ``dfhack.items.getGeneralRef(item, type)`` diff --git a/Lua API.html b/Lua API.html index d44d82bab..7ce777c42 100644 --- a/Lua API.html +++ b/Lua API.html @@ -935,7 +935,7 @@ a lua list containing them.

Units module

  • dfhack.units.getPosition(unit)

    -

    Returns true x,y,z of the unit; may be not equal to unit.pos if caged.

    +

    Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.

  • dfhack.units.getContainer(unit)

    Returns the container (cage) item or nil.

    @@ -982,7 +982,7 @@ or raws. The ignore_noble boolean disables the

    Items module

    • dfhack.items.getPosition(item)

      -

      Returns true x,y,z of the item; may be not equal to item.pos if in inventory.

      +

      Returns true x,y,z of the item, or nil if invalid; may be not equal to item.pos if in inventory.

    • dfhack.items.getGeneralRef(item, type)

      Searches for a general_ref with the given type.

      diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index f1d495bf1..335d7d8fc 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -69,6 +69,7 @@ using namespace std; #include "df/general_ref_unit_itemownerst.h" #include "df/general_ref_contains_itemst.h" #include "df/general_ref_contained_in_itemst.h" +#include "df/vermin.h" using namespace DFHack; using namespace df::enums; @@ -512,9 +513,12 @@ df::coord Items::getPosition(df::item *item) { CHECK_NULL_POINTER(item); - if (item->flags.bits.in_inventory || - item->flags.bits.in_chest || - item->flags.bits.in_building) + /* Function reverse-engineered from DF code. */ + + if (item->flags.bits.removed) + return df::coord(); + + if (item->flags.bits.in_inventory) { for (size_t i = 0; i < item->itemrefs.size(); i++) { @@ -532,15 +536,31 @@ df::coord Items::getPosition(df::item *item) return Units::getPosition(unit); break; - case general_ref_type::BUILDING_HOLDER: + /*case general_ref_type::BUILDING_HOLDER: if (auto bld = ref->getBuilding()) return df::coord(bld->centerx, bld->centery, bld->z); + break;*/ + + default: break; + } + } + + for (size_t i = 0; i < item->specific_refs.size(); i++) + { + df::specific_ref *ref = item->specific_refs[i]; + + switch (ref->type) + { + case specific_ref_type::VERMIN_ESCAPED_PET: + return ref->vermin->pos; default: break; } } + + return df::coord(); } return item->pos; @@ -625,6 +645,10 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: CHECK_NULL_POINTER(item); CHECK_NULL_POINTER(container); + auto cpos = getPosition(container); + if (!cpos.isValid()) + return false; + if (!detachItem(mc, item)) return false; @@ -635,7 +659,7 @@ bool DFHack::Items::moveToContainer(MapExtras::MapCache &mc, df::item *item, df: { delete ref1; delete ref2; Core::printerr("Could not allocate container refs.\n"); - putOnGround(mc, item, getPosition(container)); + putOnGround(mc, item, cpos); return false; } From e9ef9b87b575b4746e2090556c192237c95f508a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:04:09 +0400 Subject: [PATCH 3/5] Add central locations for onUpdate and onStateChange handling in core. --- library/Core.cpp | 35 ++++++++++++++++++++++----------- library/PluginManager.cpp | 2 -- library/include/Core.h | 14 +++++++++++++ library/include/PluginManager.h | 10 ---------- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 7f08f402f..1d2a76026 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1017,7 +1017,7 @@ int Core::Update() Lua::Core::Reset(out, "DF code execution"); if (first_update) - plug_mgr->OnStateChange(out, SC_CORE_INITIALIZED); + onStateChange(out, SC_CORE_INITIALIZED); // detect if the game was loaded or unloaded in the meantime void *new_wdata = NULL; @@ -1043,11 +1043,11 @@ int Core::Update() // and if the world is going away, we report the map change first if(had_map) - plug_mgr->OnStateChange(out, SC_MAP_UNLOADED); + onStateChange(out, SC_MAP_UNLOADED); // and if the world is appearing, we report map change after that - plug_mgr->OnStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); + onStateChange(out, new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED); if(isMapLoaded()) - plug_mgr->OnStateChange(out, SC_MAP_LOADED); + onStateChange(out, SC_MAP_LOADED); } // otherwise just check for map change... else if (new_mapdata != last_local_map_ptr) @@ -1058,7 +1058,7 @@ int Core::Update() if (isMapLoaded() != had_map) { getWorld()->ClearPersistentCache(); - plug_mgr->OnStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); + onStateChange(out, new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED); } } @@ -1071,15 +1071,12 @@ int Core::Update() if (screen != top_viewscreen) { top_viewscreen = screen; - plug_mgr->OnStateChange(out, SC_VIEWSCREEN_CHANGED); + onStateChange(out, SC_VIEWSCREEN_CHANGED); } } - // notify all the plugins that a game tick is finished - plug_mgr->OnUpdate(out); - - // process timers in lua - Lua::Core::onUpdate(out); + // Execute per-frame handlers + onUpdate(out); // Release the fake suspend lock { @@ -1116,6 +1113,22 @@ int Core::Update() return 0; }; +void Core::onUpdate(color_ostream &out) +{ + // notify all the plugins that a game tick is finished + plug_mgr->OnUpdate(out); + + // process timers in lua + Lua::Core::onUpdate(out); +} + +void Core::onStateChange(color_ostream &out, state_change_event event) +{ + plug_mgr->OnStateChange(out, event); + + Lua::Core::onStateChange(out, event); +} + // FIXME: needs to terminate the IO threads and properly dismantle all the machinery involved. int Core::Shutdown ( void ) { diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 320e8dec8..ae8cc755f 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -609,8 +609,6 @@ void PluginManager::OnStateChange(color_ostream &out, state_change_event event) { all_plugins[i]->on_state_change(out, event); } - - Lua::Core::onStateChange(out, event); } // FIXME: doesn't check name collisions! diff --git a/library/include/Core.h b/library/include/Core.h index fe83715cf..7da633cca 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -68,6 +68,17 @@ namespace DFHack class df_window; } + enum state_change_event + { + SC_WORLD_LOADED = 0, + SC_WORLD_UNLOADED = 1, + SC_MAP_LOADED = 2, + SC_MAP_UNLOADED = 3, + SC_VIEWSCREEN_CHANGED = 4, + SC_CORE_INITIALIZED = 5, + SC_BEGIN_UNLOAD = 6 + }; + // Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF. // There should never be more than one instance // Better than tracking some weird variables all over the place. @@ -162,6 +173,9 @@ namespace DFHack int SDL_Event(SDL::Event* event); bool ncurses_wgetch(int in, int & out); + void onUpdate(color_ostream &out); + void onStateChange(color_ostream &out, state_change_event event); + Core(Core const&); // Don't Implement void operator=(Core const&); // Don't implement diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 7f98bf422..02974f36b 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -65,16 +65,6 @@ namespace DFHack // Close a plugin library void ClosePlugin (DFLibrary * plugin); - enum state_change_event - { - SC_WORLD_LOADED = 0, - SC_WORLD_UNLOADED = 1, - SC_MAP_LOADED = 2, - SC_MAP_UNLOADED = 3, - SC_VIEWSCREEN_CHANGED = 4, - SC_CORE_INITIALIZED = 5, - SC_BEGIN_UNLOAD = 6 - }; struct DFHACK_EXPORT CommandReg { const char *name; int (*command)(lua_State*); From f37f708b37bf21ded2ca99fac6db3facd7a05fcf Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:38:27 +0400 Subject: [PATCH 4/5] Add a workaround for Planepacked bug in buildings constructed via API. Buildings hanging in the air cause constructWithFilters to exhibit the same behavior as a moody dwarf in a burrow excluding the workshop, i.e. endlessly collecting the same type of reagent. http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 The workaround monitors jobs and reclassifies the reagents on the fly. --- library/Core.cpp | 13 +++++++++ library/modules/Buildings.cpp | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/library/Core.cpp b/library/Core.cpp index 1d2a76026..e8c43b7f5 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -71,6 +71,7 @@ using namespace DFHack; using namespace tthread; using namespace df::enums; using df::global::init; +using df::global::world; // FIXME: A lot of code in one file, all doing different things... there's something fishy about it. @@ -1113,8 +1114,18 @@ int Core::Update() return 0; }; +extern bool buildings_do_onupdate; +void buildings_onStateChange(color_ostream &out, state_change_event event); +void buildings_onUpdate(color_ostream &out); + +static int buildings_timer = 0; + void Core::onUpdate(color_ostream &out) { + // convert building reagents + if (buildings_do_onupdate && (++buildings_timer & 1)) + buildings_onUpdate(out); + // notify all the plugins that a game tick is finished plug_mgr->OnUpdate(out); @@ -1124,6 +1135,8 @@ void Core::onUpdate(color_ostream &out) void Core::onStateChange(color_ostream &out, state_change_event event) { + buildings_onStateChange(out, event); + plug_mgr->OnStateChange(out, event); Lua::Core::onStateChange(out, event); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index d2b3e3527..e0afc56ed 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -87,6 +87,58 @@ static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) return &extent.extents[dx + dy*extent.width]; } +/* + * A monitor to work around this bug, in its application to buildings: + * + * http://www.bay12games.com/dwarves/mantisbt/view.php?id=1416 + */ +bool buildings_do_onupdate = false; + +void buildings_onStateChange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_MAP_LOADED: + buildings_do_onupdate = true; + break; + case SC_MAP_UNLOADED: + buildings_do_onupdate = false; + break; + default: + break; + } +} + +void buildings_onUpdate(color_ostream &out) +{ + buildings_do_onupdate = false; + + df::job_list_link *link = world->job_list.next; + for (; link; link = link->next) { + df::job *job = link->item; + + if (job->job_type != job_type::ConstructBuilding) + continue; + if (job->job_items.empty()) + continue; + + buildings_do_onupdate = true; + + for (size_t i = 0; i < job->items.size(); i++) + { + df::job_item_ref *iref = job->items[i]; + if (iref->role != df::job_item_ref::Reagent) + continue; + df::job_item *item = vector_get(job->job_items, iref->job_item_idx); + if (!item) + continue; + // Convert Reagent to Hauled, while decrementing quantity + item->quantity = std::max(0, item->quantity-1); + iref->role = df::job_item_ref::Hauled; + iref->job_item_idx = -1; + } + } +} + uint32_t Buildings::getNumBuildings() { return world->buildings.all.size(); @@ -906,6 +958,8 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectormat_index = items[i]->mat_index; } + buildings_do_onupdate = true; + createDesign(bld, rough); return true; } @@ -969,3 +1023,4 @@ bool Buildings::deconstruct(df::building *bld) return true; } + From 52426f9035c54fdfcd38992259c94a65de160ed8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 17 May 2012 20:41:41 +0400 Subject: [PATCH 5/5] Track xml --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index b0d8e71dc..ab102b93f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b0d8e71dc9f6e697409bd999801097acdc57fcd8 +Subproject commit ab102b93fca9b1ae8b5fbfc3ae5a9ee06ee60811