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.
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 thedfhack.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/Core.cpp b/library/Core.cpp index 7f08f402f..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. @@ -1017,7 +1018,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 +1044,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 +1059,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 +1072,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 +1114,34 @@ 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); + + // process timers in lua + Lua::Core::onUpdate(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); +} + // 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*); 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..e0afc56ed 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; @@ -86,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(); @@ -289,6 +342,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 +649,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 +944,11 @@ bool Buildings::constructWithFilters(df::building *bld, std::vector