From bae85ac77d977ce543fc2e36f42aed3a10f79a5c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 1 Sep 2012 17:52:51 +0400 Subject: [PATCH] Make the steam engine consume liquids from Z level below. --- plugins/devel/steam-engine.cpp | 195 ++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/plugins/devel/steam-engine.cpp b/plugins/devel/steam-engine.cpp index 6f83c41d9..1f45e511a 100644 --- a/plugins/devel/steam-engine.cpp +++ b/plugins/devel/steam-engine.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -24,6 +26,9 @@ #include "df/machine.h" #include "df/job.h" #include "df/building_drawbuffer.h" +#include "df/ui.h" +#include "df/viewscreen_dwarfmodest.h" +#include "df/ui_build_selector.h" #include "MiscUtils.h" @@ -35,6 +40,7 @@ using namespace df::enums; using df::global::gps; using df::global::world; +using df::global::ui; using df::global::ui_build_selector; DFHACK_PLUGIN("steam-engine"); @@ -70,6 +76,29 @@ static const int hearth_colors[6][2] = { { COLOR_GREY, 1 } }; +void enable_updates_at(df::coord pos, bool flow, bool temp) +{ + static const int delta[4][2] = { { -1, -1 }, { 1, -1 }, { -1, 1 }, { 1, 1 } }; + + for (int i = 0; i < 4; i++) + { + auto blk = Maps::getTileBlock(pos.x+delta[i][0], pos.y+delta[i][1], pos.z); + Maps::enableBlockUpdates(blk, flow, temp); + } +} + +void decrement_flow(df::coord pos, int amount) +{ + auto pldes = Maps::getTileDesignation(pos); + if (!pldes) return; + + int nsize = std::max(0, pldes->bits.flow_size - amount); + pldes->bits.flow_size = nsize; + pldes->bits.flow_forbid = (nsize > 3 || pldes->bits.liquid_type == tile_liquid::Magma); + + enable_updates_at(pos, true, false); +} + struct liquid_hook : df::item_liquid_miscst { typedef df::item_liquid_miscst interpose_base; @@ -128,8 +157,65 @@ struct workshop_hook : df::building_workshopst { flags.whole = (flags.whole & 0x0FFFFFFFU) | uint32_t((count & 15) << 28); } - void absorb_unit(steam_engine_workshop *engine, df::item_liquid_miscst *liquid) + bool find_liquids(df::coord *pwater, df::coord *pmagma, bool is_magma, bool any_level) + { + if (!is_magma) + pmagma = NULL; + + for (int x = x1; x <= x2; x++) + { + for (int y = y1; y <= y2; y++) + { + auto ptile = Maps::getTileType(x,y,z); + if (!ptile || !LowPassable(*ptile)) + continue; + + auto pltile = Maps::getTileType(x,y,z-1); + if (!pltile || !FlowPassable(*pltile)) + continue; + + auto pldes = Maps::getTileDesignation(x,y,z-1); + if (!pldes || pldes->bits.flow_size == 0) + continue; + + if (pldes->bits.liquid_type == tile_liquid::Magma) + { + if (!pmagma || (!any_level && pldes->bits.flow_size < 4)) + continue; + + *pmagma = df::coord(x,y,z-1); + if (pwater->isValid()) + return true; + } + else + { + if (!any_level && pldes->bits.flow_size < 3) + continue; + + *pwater = df::coord(x,y,z-1); + if (!pmagma || pmagma->isValid()) + return true; + } + } + } + + return false; + } + + bool absorb_unit(steam_engine_workshop *engine, df::item_liquid_miscst *liquid) { + df::coord water, magma; + + if (!find_liquids(&water, &magma, engine->is_magma, true)) + { + liquid->addWear(WEAR_TICKS*4+1, true, false); + return false; + } + + decrement_flow(water, 1); + if (engine->is_magma) + decrement_flow(magma, 1); + liquid->flags.bits.in_building = true; liquid->mat_state.whole |= liquid_hook::BOILING_FLAG; liquid->temperature = liquid->getBoilingPoint()-1; @@ -138,6 +224,9 @@ struct workshop_hook : df::building_workshopst { // This affects where the steam appears to come from if (engine->hearth_tile.isValid()) liquid->pos = df::coord(x1+engine->hearth_tile.x, y1+engine->hearth_tile.y, z); + + enable_updates_at(liquid->pos, false, true); + return true; } bool boil_unit(df::item_liquid_miscst *liquid) @@ -178,7 +267,8 @@ struct workshop_hook : df::building_workshopst { liquid->wear != 0) continue; - absorb_unit(engine, liquid); + if (!absorb_unit(engine, liquid)) + continue; } if (*count < engine->max_capacity) @@ -330,6 +420,18 @@ struct workshop_hook : df::building_workshopst { return INTERPOSE_NEXT(canConnectToMachine)(info); } + // Operation logic + DEFINE_VMETHOD_INTERPOSE(bool, isUnpowered, ()) + { + if (auto engine = get_steam_engine()) + { + df::coord water, magma; + return !find_liquids(&water, &magma, engine->is_magma, false); + } + + return INTERPOSE_NEXT(isUnpowered)(); + } + DEFINE_VMETHOD_INTERPOSE(void, updateAction, ()) { if (auto engine = get_steam_engine()) @@ -402,6 +504,18 @@ struct workshop_hook : df::building_workshopst { db->fore[pos.x][pos.y] = hearth_colors[power][0]; db->bright[pos.x][pos.y] = hearth_colors[power][1]; } + + // Set liquid indicator state + if (engine->water_tile.isValid() || engine->magma_tile.isValid()) + { + df::coord water, magma; + find_liquids(&water, &magma, engine->is_magma, false); + + if (engine->water_tile.isValid() && !water.isValid()) + db->fore[engine->water_tile.x][engine->water_tile.y] = 0; + if (engine->magma_tile.isValid() && engine->is_magma && !magma.isValid()) + db->fore[engine->magma_tile.x][engine->magma_tile.y] = 0; + } } } @@ -421,10 +535,84 @@ IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, isPowerSource); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, categorize); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, uncategorize); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, canConnectToMachine); +IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, isUnpowered); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, updateAction); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, drawBuilding); IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, deconstructItems); +struct dwarfmode_hook : df::viewscreen_dwarfmodest +{ + typedef df::viewscreen_dwarfmodest interpose_base; + + steam_engine_workshop *get_steam_engine() + { + if (ui->main.mode == ui_sidebar_mode::Build && + ui_build_selector->stage == 1 && + ui_build_selector->building_type == building_type::Workshop && + ui_build_selector->building_subtype == workshop_type::Custom) + { + return find_steam_engine(ui_build_selector->custom_type); + } + + return NULL; + } + + void check_hanging_tiles(steam_engine_workshop *engine) + { + using df::global::cursor; + + if (!engine) return; + + bool error = false; + + int x1 = cursor->x - engine->def->workloc_x; + int y1 = cursor->y - engine->def->workloc_y; + + for (int x = 0; x < engine->def->dim_x; x++) + { + for (int y = 0; y < engine->def->dim_y; y++) + { + if (ui_build_selector->tiles[x][y] >= 5) + continue; + + auto ptile = Maps::getTileType(x1+x,y1+y,cursor->z); + if (ptile && !isOpenTerrain(*ptile)) + continue; + + ui_build_selector->tiles[x][y] = 6; + error = true; + } + } + + if (error) + { + const char *msg = "Hanging - use down stair."; + ui_build_selector->errors.push_back(new std::string(msg)); + } + } + + DEFINE_VMETHOD_INTERPOSE(void, feed, (set *input)) + { + steam_engine_workshop *engine = get_steam_engine(); + + // Selector insists that workshops cannot be placed hanging + // unless they require magma, so pretend we always do. + if (engine) + engine->def->needs_magma = true; + + INTERPOSE_NEXT(feed)(input); + + // Restore the flag + if (engine) + engine->def->needs_magma = engine->is_magma; + + // And now, check for open space + check_hanging_tiles(get_steam_engine()); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_hook, feed); + static bool find_engines() { engines.clear(); @@ -492,9 +680,12 @@ static void enable_hooks(bool enable) INTERPOSE_HOOK(workshop_hook, categorize).apply(enable); INTERPOSE_HOOK(workshop_hook, uncategorize).apply(enable); INTERPOSE_HOOK(workshop_hook, canConnectToMachine).apply(enable); + INTERPOSE_HOOK(workshop_hook, isUnpowered).apply(enable); INTERPOSE_HOOK(workshop_hook, updateAction).apply(enable); INTERPOSE_HOOK(workshop_hook, drawBuilding).apply(enable); INTERPOSE_HOOK(workshop_hook, deconstructItems).apply(enable); + + INTERPOSE_HOOK(dwarfmode_hook, feed).apply(enable); } DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)