summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-09-06 17:10:58 +0400
committerAlexander Gavrilov2012-09-06 17:10:58 +0400
commitd0e630d4c35717bad682894e33e7dd57f86ac126 (patch)
tree26b77252d249b651c4dd8eb3647f9596a10ced62 /plugins/devel
parentd5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a (diff)
downloaddfhack-d0e630d4c35717bad682894e33e7dd57f86ac126.tar.gz
dfhack-d0e630d4c35717bad682894e33e7dd57f86ac126.tar.bz2
dfhack-d0e630d4c35717bad682894e33e7dd57f86ac126.tar.xz
Move steam engine out of devel, since it should be fully functional.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/CMakeLists.txt1
-rw-r--r--plugins/devel/building_steam_engine.txt92
-rw-r--r--plugins/devel/item_trapcomp_steam_engine.txt12
-rw-r--r--plugins/devel/reaction_steam_engine.txt14
-rw-r--r--plugins/devel/steam-engine.cpp1007
5 files changed, 0 insertions, 1126 deletions
diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt
index f126ae53..134d5cb6 100644
--- a/plugins/devel/CMakeLists.txt
+++ b/plugins/devel/CMakeLists.txt
@@ -18,7 +18,6 @@ DFHACK_PLUGIN(stripcaged stripcaged.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)
DFHACK_PLUGIN(vshook vshook.cpp)
-DFHACK_PLUGIN(steam-engine steam-engine.cpp)
IF(UNIX)
DFHACK_PLUGIN(ref-index ref-index.cpp)
ENDIF()
diff --git a/plugins/devel/building_steam_engine.txt b/plugins/devel/building_steam_engine.txt
deleted file mode 100644
index 48657b0c..00000000
--- a/plugins/devel/building_steam_engine.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-building_steam_engine
-
-[OBJECT:BUILDING]
-
-[BUILDING_WORKSHOP:STEAM_ENGINE]
- [NAME:Steam Engine]
- [NAME_COLOR:4:0:1]
- [DIM:3:3]
- [WORK_LOCATION:2:3]
- [BUILD_LABOR:MECHANIC]
- [BUILD_KEY:CUSTOM_ALT_S]
- [BLOCK:1:1:1:1]
- [BLOCK:2:1:1:1]
- [BLOCK:3:1:0:1]
- [TILE:0:1:240:' ':254]
- [TILE:0:2:' ':' ':128]
- [TILE:0:3:246:' ':' ']
- [COLOR:0:1:6:0:0:0:0:0:7:0:0]
- [COLOR:0:2:0:0:0:0:0:0:7:0:0]
- [COLOR:0:3:MAT:0:0:0:0:0:0]
- [TILE:1:1:246:128:' ']
- [TILE:1:2:' ':' ':254]
- [TILE:1:3:254:'/':240]
- [COLOR:1:1:MAT:7:0:0:0:0:0]
- [COLOR:1:2:0:0:0:0:0:0:7:0:0]
- [COLOR:1:3:7:0:0:6:0:0:6:0:0]
- [TILE:2:1:21:' ':128]
- [TILE:2:2:128:' ':246]
- [TILE:2:3:177:19:177]
- [COLOR:2:1:6:0:0:0:0:0:7:0:0]
- [COLOR:2:2:7:0:0:0:0:0:MAT]
- [COLOR:2:3:7:0:0:6:0:0:7:0:0]
- Tile 15 marks places where machines can connect.
- Tile 19 marks the hearth (color changed to reflect power).
- [TILE:3:1:15:246:15]
- [TILE:3:2:'\':19:'/']
- [TILE:3:3:7:' ':7]
- Color 1:?:1 water indicator, 4:?:1 magma indicator:
- [COLOR:3:1:7:0:0:MAT:7:0:0]
- [COLOR:3:2:6:0:0:0:0:1:6:0:0]
- [COLOR:3:3:1:7:1:0:0:0:4:7:1]
- [BUILD_ITEM:1:BARREL:NONE:INORGANIC:NONE][EMPTY][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:PIPE_SECTION:NONE:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:TRAPCOMP:ITEM_TRAPCOMP_STEAM_PISTON:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:CHAIN:NONE:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:TRAPPARTS:NONE:NONE:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:BLOCKS:NONE:NONE:NONE][BUILDMAT][FIRE_BUILD_SAFE]
-
-[BUILDING_WORKSHOP:MAGMA_STEAM_ENGINE]
- [NAME:Magma Steam Engine]
- [NAME_COLOR:4:0:1]
- [DIM:3:3]
- [WORK_LOCATION:2:3]
- [BUILD_LABOR:MECHANIC]
- [BUILD_KEY:CUSTOM_ALT_E]
- [NEEDS_MAGMA]
- [BLOCK:1:1:1:1]
- [BLOCK:2:1:1:1]
- [BLOCK:3:1:0:1]
- [TILE:0:1:240:' ':254]
- [TILE:0:2:' ':' ':128]
- [TILE:0:3:246:' ':' ']
- [COLOR:0:1:6:0:0:0:0:0:7:0:0]
- [COLOR:0:2:0:0:0:0:0:0:7:0:0]
- [COLOR:0:3:MAT:0:0:0:0:0:0]
- [TILE:1:1:246:128:' ']
- [TILE:1:2:' ':' ':254]
- [TILE:1:3:254:'/':240]
- [COLOR:1:1:MAT:7:0:0:0:0:0]
- [COLOR:1:2:0:0:0:0:0:0:7:0:0]
- [COLOR:1:3:7:0:0:6:0:0:6:0:0]
- [TILE:2:1:21:' ':128]
- [TILE:2:2:128:' ':246]
- [TILE:2:3:177:19:177]
- [COLOR:2:1:6:0:0:0:0:0:7:0:0]
- [COLOR:2:2:7:0:0:0:0:0:MAT]
- [COLOR:2:3:7:0:0:6:0:0:7:0:0]
- Tile 15 marks places where machines can connect.
- Tile 19 marks the hearth (color changed to reflect power).
- [TILE:3:1:15:246:15]
- [TILE:3:2:'\':19:'/']
- [TILE:3:3:7:' ':7]
- Color 1:?:1 water indicator, 4:?:1 magma indicator:
- [COLOR:3:1:7:0:0:MAT:7:0:0]
- [COLOR:3:2:6:0:0:0:0:1:6:0:0]
- [COLOR:3:3:1:7:1:0:0:0:4:7:1]
- [BUILD_ITEM:1:BARREL:NONE:INORGANIC:NONE][EMPTY][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:PIPE_SECTION:NONE:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:TRAPCOMP:ITEM_TRAPCOMP_STEAM_PISTON:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:CHAIN:NONE:INORGANIC:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:TRAPPARTS:NONE:NONE:NONE][CAN_USE_ARTIFACT]
- [BUILD_ITEM:1:BLOCKS:NONE:NONE:NONE][BUILDMAT][MAGMA_BUILD_SAFE]
diff --git a/plugins/devel/item_trapcomp_steam_engine.txt b/plugins/devel/item_trapcomp_steam_engine.txt
deleted file mode 100644
index c35f6ef4..00000000
--- a/plugins/devel/item_trapcomp_steam_engine.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-item_trapcomp_steam_engine
-
-[OBJECT:ITEM]
-
-[ITEM_TRAPCOMP:ITEM_TRAPCOMP_STEAM_PISTON]
-[NAME:piston:pistons]
-[ADJECTIVE:heavy]
-[SIZE:1800]
-[HITS:1]
-[MATERIAL_SIZE:6]
-[METAL]
-[ATTACK:BLUNT:40:200:bash:bashes:NO_SUB:2000]
diff --git a/plugins/devel/reaction_steam_engine.txt b/plugins/devel/reaction_steam_engine.txt
deleted file mode 100644
index 175ffdd5..00000000
--- a/plugins/devel/reaction_steam_engine.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-reaction_steam_engine
-
-[OBJECT:REACTION]
-
-[REACTION:STOKE_BOILER]
- [NAME:stoke the boiler]
- [BUILDING:STEAM_ENGINE:CUSTOM_S]
- [BUILDING:MAGMA_STEAM_ENGINE:CUSTOM_S]
- [FUEL]
- [SKILL:SMELT]
- Dimension is the number of days it can produce 100 power * 100.
- I.e. with 2000 it means energy of 1 job = 1 water wheel for 20 days.
- [PRODUCT:100:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:2000]
-
diff --git a/plugins/devel/steam-engine.cpp b/plugins/devel/steam-engine.cpp
deleted file mode 100644
index cacfc6e1..00000000
--- a/plugins/devel/steam-engine.cpp
+++ /dev/null
@@ -1,1007 +0,0 @@
-#include "Core.h"
-#include <Console.h>
-#include <Export.h>
-#include <PluginManager.h>
-#include <modules/Gui.h>
-#include <modules/Screen.h>
-#include <modules/Maps.h>
-#include <TileTypes.h>
-#include <vector>
-#include <cstdio>
-#include <stack>
-#include <string>
-#include <cmath>
-#include <string.h>
-
-#include <VTableInterpose.h>
-#include "df/graphic.h"
-#include "df/building_workshopst.h"
-#include "df/building_def_workshopst.h"
-#include "df/item_liquid_miscst.h"
-#include "df/power_info.h"
-#include "df/workshop_type.h"
-#include "df/builtin_mats.h"
-#include "df/world.h"
-#include "df/buildings_other_id.h"
-#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 "df/flow_info.h"
-#include "df/report.h"
-
-#include "MiscUtils.h"
-
-/*
- * This plugin implements a steam engine workshop. It activates
- * if there are any workshops in the raws with STEAM_ENGINE in
- * their token, and provides the necessary behavior.
- *
- * Construction:
- *
- * The workshop needs water as its input, which it takes via a
- * passable floor tile below it, like usual magma workshops do.
- * The magma version also needs magma.
- *
- * ISSUE: Since this building is a machine, and machine collapse
- * code cannot be modified, it would collapse over true open space.
- * As a loophole, down stair provides support to machines, while
- * being passable, so use them.
- *
- * After constructing the building itself, machines can be connected
- * to the edge tiles that look like gear boxes. Their exact position
- * is extracted from the workshop raws.
- *
- * ISSUE: Like with collapse above, part of the code involved in
- * machine connection cannot be modified. As a result, the workshop
- * can only immediately connect to machine components built AFTER it.
- * This also means that engines cannot be chained without intermediate
- * short axles that can be built later.
- *
- * Operation:
- *
- * In order to operate the engine, queue the Stoke Boiler job.
- * A furnace operator will come, possibly bringing a bar of fuel,
- * and perform it. As a result, a "boiling water" item will appear
- * in the 't' view of the workshop.
- *
- * Note: The completion of the job will actually consume one unit
- * of appropriate liquids from below the workshop.
- *
- * Every such item gives 100 power, up to a limit of 300 for coal,
- * and 500 for a magma engine. The building can host twice that
- * amount of items to provide longer autonomous running. When the
- * boiler gets filled to capacity, all queued jobs are suspended;
- * once it drops back to 3+1 or 5+1 items, they are re-enabled.
- *
- * While the engine is providing power, steam is being consumed.
- * The consumption speed includes a fixed 10% waste rate, and
- * the remaining 90% are applied proportionally to the actual
- * load in the machine. With the engine at nominal 300 power with
- * 150 load in the system, it will consume steam for actual
- * 300*(10% + 90%*150/300) = 165 power.
- *
- * Masterpiece mechanism and chain will decrease the mechanical
- * power drawn by the engine itself from 10 to 5. Masterpiece
- * barrel decreases waste rate by 4%. Masterpiece piston and pipe
- * decrease it by further 4%, and also decrease the whole steam
- * use rate by 10%.
- *
- * Explosions:
- *
- * The engine must be constructed using barrel, pipe and piston
- * from fire-safe, or in the magma version magma-safe metals.
- *
- * During operation weak parts get gradually worn out, and
- * eventually the engine explodes. It should also explode if
- * toppled during operation by a building destroyer, or a
- * tantruming dwarf.
- *
- * Save files:
- *
- * It should be safe to load and view fortresses using engines
- * from a DF version without DFHack installed, except that in such
- * case the engines won't work. However actually making modifications
- * to them, or machines they connect to (including by pulling levers),
- * can easily result in inconsistent state once this plugin is
- * available again. The effects may be as weird as negative power
- * being generated.
- */
-
-using std::vector;
-using std::string;
-using std::stack;
-using namespace DFHack;
-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");
-
-/*
- * List of known steam engine workshop raws.
- */
-
-struct steam_engine_workshop {
- int id;
- df::building_def_workshopst *def;
- // Cached properties
- bool is_magma;
- int max_power, max_capacity;
- int wear_temp;
- // Special tiles (relative position)
- std::vector<df::coord2d> gear_tiles;
- df::coord2d hearth_tile;
- df::coord2d water_tile;
- df::coord2d magma_tile;
-};
-
-std::vector<steam_engine_workshop> engines;
-
-steam_engine_workshop *find_steam_engine(int id)
-{
- for (size_t i = 0; i < engines.size(); i++)
- if (engines[i].id == id)
- return &engines[i];
-
- return NULL;
-}
-
-/*
- * Misc utilities.
- */
-
-static const int hearth_colors[6][2] = {
- { COLOR_BLACK, 1 },
- { COLOR_BROWN, 0 },
- { COLOR_RED, 0 },
- { COLOR_RED, 1 },
- { COLOR_BROWN, 1 },
- { 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, int(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);
-}
-
-void make_explosion(df::coord center, int power)
-{
- static const int bias[9] = {
- 60, 30, 60,
- 30, 0, 30,
- 60, 30, 60
- };
-
- int mat_type = builtin_mats::WATER, mat_index = -1;
- int i = 0;
-
- for (int dx = -1; dx <= 1; dx++)
- {
- for (int dy = -1; dy <= 1; dy++)
- {
- int size = power - bias[i++];
- auto pos = center + df::coord(dx,dy,0);
-
- if (size > 0)
- Maps::spawnFlow(pos, flow_type::MaterialDust, mat_type, mat_index, size);
- }
- }
-
- Gui::showAutoAnnouncement(
- announcement_type::CAVE_COLLAPSE, center,
- "A boiler has exploded!", COLOR_RED, true
- );
-}
-
-static const int WEAR_TICKS = 806400;
-
-bool add_wear_nodestroy(df::item_actual *item, int rate)
-{
- if (item->incWearTimer(rate))
- {
- while (item->wear_timer >= WEAR_TICKS)
- {
- item->wear_timer -= WEAR_TICKS;
- item->wear++;
- }
- }
-
- return item->wear > 3;
-}
-
-/*
- * Hook for the liquid item. Implements a special 'boiling'
- * matter state with a modified description and temperature
- * locked at boiling-1.
- */
-
-struct liquid_hook : df::item_liquid_miscst {
- typedef df::item_liquid_miscst interpose_base;
-
- static const uint32_t BOILING_FLAG = 0x80000000U;
-
- DEFINE_VMETHOD_INTERPOSE(void, getItemDescription, (std::string *buf, int8_t mode))
- {
- if (mat_state.whole & BOILING_FLAG)
- buf->append("boiling ");
-
- INTERPOSE_NEXT(getItemDescription)(buf, mode);
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, adjustTemperature, (uint16_t temp, int32_t unk))
- {
- if (mat_state.whole & BOILING_FLAG)
- temp = std::max(int(temp), getBoilingPoint()-1);
-
- return INTERPOSE_NEXT(adjustTemperature)(temp, unk);
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, checkTemperatureDamage, ())
- {
- if (mat_state.whole & BOILING_FLAG)
- temperature = std::max(int(temperature), getBoilingPoint()-1);
-
- return INTERPOSE_NEXT(checkTemperatureDamage)();
- }
-};
-
-IMPLEMENT_VMETHOD_INTERPOSE(liquid_hook, getItemDescription);
-IMPLEMENT_VMETHOD_INTERPOSE(liquid_hook, adjustTemperature);
-IMPLEMENT_VMETHOD_INTERPOSE(liquid_hook, checkTemperatureDamage);
-
-/*
- * Hook for the workshop itself. Implements core logic.
- */
-
-struct workshop_hook : df::building_workshopst {
- typedef df::building_workshopst interpose_base;
-
- // Engine detection
-
- steam_engine_workshop *get_steam_engine()
- {
- if (type == workshop_type::Custom)
- return find_steam_engine(custom_type);
-
- return NULL;
- }
-
- inline bool is_fully_built()
- {
- return getBuildStage() >= getMaxBuildStage();
- }
-
- // Use high bits of flags to store current steam amount.
- // This is necessary for consistency if items disappear unexpectedly.
-
- int get_steam_amount()
- {
- return (flags.whole >> 28) & 15;
- }
-
- void set_steam_amount(int count)
- {
- flags.whole = (flags.whole & 0x0FFFFFFFU) | uint32_t((count & 15) << 28);
- }
-
- // Find liquids to consume below the engine.
-
- bool find_liquids(df::coord *pwater, df::coord *pmagma, bool is_magma, int min_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 < min_level)
- continue;
-
- if (pldes->bits.liquid_type == tile_liquid::Magma)
- {
- if (pmagma)
- *pmagma = df::coord(x,y,z-1);
- if (pwater->isValid())
- return true;
- }
- else
- {
- *pwater = df::coord(x,y,z-1);
- if (!pmagma || pmagma->isValid())
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Absorbs a water item produced by stoke reaction into the engine.
-
- bool absorb_unit(steam_engine_workshop *engine, df::item_liquid_miscst *liquid)
- {
- // Consume liquid inputs
- df::coord water, magma;
-
- if (!find_liquids(&water, &magma, engine->is_magma, 1))
- {
- // Destroy the item with enormous wear amount.
- liquid->addWear(WEAR_TICKS*5, true, false);
- return false;
- }
-
- decrement_flow(water, 1);
- if (engine->is_magma)
- decrement_flow(magma, 1);
-
- // Update flags
- liquid->flags.bits.in_building = true;
- liquid->mat_state.whole |= liquid_hook::BOILING_FLAG;
- liquid->temperature = liquid->getBoilingPoint()-1;
- liquid->temperature_fraction = 0;
-
- // 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 block temperature updates
- enable_updates_at(liquid->pos, false, true);
- return true;
- }
-
- bool boil_unit(df::item_liquid_miscst *liquid)
- {
- liquid->wear = 4;
- liquid->flags.bits.in_building = false;
- liquid->temperature = liquid->getBoilingPoint() + 10;
-
- return liquid->checkMeltBoil();
- }
-
- void suspend_jobs(bool suspend)
- {
- for (size_t i = 0; i < jobs.size(); i++)
- if (jobs[i]->job_type == job_type::CustomReaction)
- jobs[i]->flags.bits.suspend = suspend;
- }
-
- // Scan contained items for boiled steam to absorb.
-
- df::item_liquid_miscst *collect_steam(steam_engine_workshop *engine, int *count)
- {
- df::item_liquid_miscst *first = NULL;
- *count = 0;
-
- for (int i = contained_items.size()-1; i >= 0; i--)
- {
- auto item = contained_items[i];
- if (item->use_mode != 0)
- continue;
-
- auto liquid = strict_virtual_cast<df::item_liquid_miscst>(item->item);
- if (!liquid)
- continue;
-
- if (!liquid->flags.bits.in_building)
- {
- if (liquid->mat_type != builtin_mats::WATER ||
- liquid->age > 1 ||
- liquid->wear != 0)
- continue;
-
- // This may destroy the item
- if (!absorb_unit(engine, liquid))
- continue;
- }
-
- if (*count < engine->max_capacity)
- {
- first = liquid;
- ++*count;
- }
- else
- {
- // Overpressure valve
- boil_unit(liquid);
- suspend_jobs(true);
- }
- }
-
- return first;
- }
-
- void random_boil()
- {
- int cnt = 0;
-
- for (int i = contained_items.size()-1; i >= 0; i--)
- {
- auto item = contained_items[i];
- if (item->use_mode != 0 || !item->item->flags.bits.in_building)
- continue;
-
- auto liquid = strict_virtual_cast<df::item_liquid_miscst>(item->item);
- if (!liquid)
- continue;
-
- if (cnt == 0 || rand() < RAND_MAX/2)
- {
- cnt++;
- boil_unit(liquid);
- }
- }
- }
-
- int classify_component(df::building_actual::T_contained_items *item)
- {
- if (item->use_mode != 2 || item->item->isBuildMat())
- return -1;
-
- switch (item->item->getType())
- {
- case item_type::TRAPPARTS:
- case item_type::CHAIN:
- return 0;
- case item_type::BARREL:
- return 2;
- default:
- return 1;
- }
- }
-
- bool check_component_wear(steam_engine_workshop *engine, int count, int power)
- {
- int coeffs[3] = { 0, power, count };
-
- for (int i = contained_items.size()-1; i >= 0; i--)
- {
- int type = classify_component(contained_items[i]);
- if (type < 0)
- continue;
-
- df::item *item = contained_items[i]->item;
- int melt_temp = item->getMeltingPoint();
- if (coeffs[type] == 0 || melt_temp >= engine->wear_temp)
- continue;
-
- // let 500 degree delta at 4 pressure work 1 season
- float ticks = coeffs[type]*(engine->wear_temp - melt_temp)*3.0f/500.0f/4.0f;
- if (item->addWear(int(8*(1 + ticks)), true, true))
- return true;
- }
-
- return false;
- }
-
- float get_component_quality(int use_type)
- {
- float sum = 0, cnt = 0;
-
- for (size_t i = 0; i < contained_items.size(); i++)
- {
- int type = classify_component(contained_items[i]);
- if (type != use_type)
- continue;
-
- sum += contained_items[i]->item->getQuality();
- cnt += 1;
- }
-
- return (cnt > 0 ? sum/cnt : 0);
- }
-
- int get_steam_use_rate(steam_engine_workshop *engine, int dimension, int power_level)
- {
- // total ticks to wear off completely
- float ticks = WEAR_TICKS * 4.0f;
- // dimension == days it lasts * 100
- ticks /= 1200.0f * dimension / 100.0f;
- // true power use
- float power_rate = 1.0f;
- // check the actual load
- if (auto mptr = df::machine::find(machine.machine_id))
- {
- if (mptr->cur_power >= mptr->min_power)
- power_rate = float(mptr->min_power) / mptr->cur_power;
- else
- power_rate = 0.0f;
- }
- // waste rate: 1-10% depending on piston assembly quality
- float piston_qual = get_component_quality(1);
- float waste = 0.1f - 0.016f * 0.5f * (piston_qual + get_component_quality(2));
- float efficiency_coeff = 1.0f - 0.02f * piston_qual;
- // apply rate and waste factor
- ticks *= (waste + 0.9f*power_rate)*power_level*efficiency_coeff;
- // end result
- return std::max(1, int(ticks));
- }
-
- void update_under_construction(steam_engine_workshop *engine)
- {
- if (machine.machine_id != -1)
- return;
-
- int cur_count = 0;
-
- if (auto first = collect_steam(engine, &cur_count))
- {
- if (add_wear_nodestroy(first, WEAR_TICKS*4/10))
- {
- boil_unit(first);
- cur_count--;
- }
- }
-
- set_steam_amount(cur_count);
- }
-
- void update_working(steam_engine_workshop *engine)
- {
- int old_count = get_steam_amount();
- int old_power = std::min(engine->max_power, old_count);
- int cur_count = 0;
-
- if (auto first = collect_steam(engine, &cur_count))
- {
- int rate = get_steam_use_rate(engine, first->dimension, old_power);
-
- if (add_wear_nodestroy(first, rate))
- {
- boil_unit(first);
- cur_count--;
- }
-
- if (check_component_wear(engine, old_count, old_power))
- return;
- }
-
- if (old_count < engine->max_capacity && cur_count == engine->max_capacity)
- suspend_jobs(true);
- else if (cur_count <= engine->max_power+1 && old_count > engine->max_power+1)
- suspend_jobs(false);
-
- set_steam_amount(cur_count);
-
- int cur_power = std::min(engine->max_power, cur_count);
- if (cur_power != old_power)
- {
- auto mptr = df::machine::find(machine.machine_id);
- if (mptr)
- mptr->cur_power += (cur_power - old_power)*100;
- }
- }
-
- // Furnaces need architecture, and this is a workshop
- // only because furnaces cannot connect to machines.
- DEFINE_VMETHOD_INTERPOSE(bool, needsDesign, ())
- {
- if (get_steam_engine())
- return true;
-
- return INTERPOSE_NEXT(needsDesign)();
- }
-
- // Machine interface
- DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info))
- {
- if (auto engine = get_steam_engine())
- {
- info->produced = std::min(engine->max_power, get_steam_amount())*100;
- info->consumed = 10 - int(get_component_quality(0));
- return;
- }
-
- INTERPOSE_NEXT(getPowerInfo)(info);
- }
-
- DEFINE_VMETHOD_INTERPOSE(df::machine_info*, getMachineInfo, ())
- {
- if (get_steam_engine())
- return &machine;
-
- return INTERPOSE_NEXT(getMachineInfo)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, isPowerSource, ())
- {
- if (get_steam_engine())
- return true;
-
- return INTERPOSE_NEXT(isPowerSource)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, categorize, (bool free))
- {
- if (get_steam_engine())
- {
- auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE];
- insert_into_vector(vec, &df::building::id, (df::building*)this);
- }
-
- INTERPOSE_NEXT(categorize)(free);
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, uncategorize, ())
- {
- if (get_steam_engine())
- {
- auto &vec = world->buildings.other[buildings_other_id::ANY_MACHINE];
- erase_from_vector(vec, &df::building::id, id);
- }
-
- INTERPOSE_NEXT(uncategorize)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(bool, canConnectToMachine, (df::machine_tile_set *info))
- {
- if (auto engine = get_steam_engine())
- {
- int real_cx = centerx, real_cy = centery;
- bool ok = false;
-
- for (size_t i = 0; i < engine->gear_tiles.size(); i++)
- {
- // the original function connects to the center tile
- centerx = x1 + engine->gear_tiles[i].x;
- centery = y1 + engine->gear_tiles[i].y;
-
- if (!INTERPOSE_NEXT(canConnectToMachine)(info))
- continue;
-
- ok = true;
- break;
- }
-
- centerx = real_cx; centery = real_cy;
- return ok;
- }
- else
- 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, 3);
- }
-
- return INTERPOSE_NEXT(isUnpowered)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, updateAction, ())
- {
- if (auto engine = get_steam_engine())
- {
- if (is_fully_built())
- update_working(engine);
- else
- update_under_construction(engine);
-
- if (flags.bits.almost_deleted)
- return;
- }
-
- INTERPOSE_NEXT(updateAction)();
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk))
- {
- INTERPOSE_NEXT(drawBuilding)(db, unk);
-
- if (auto engine = get_steam_engine())
- {
- if (!is_fully_built())
- return;
-
- // If machine is running, tweak gear assemblies
- auto mptr = df::machine::find(machine.machine_id);
- if (mptr && (mptr->visual_phase & 1) != 0)
- {
- for (size_t i = 0; i < engine->gear_tiles.size(); i++)
- {
- auto pos = engine->gear_tiles[i];
- db->tile[pos.x][pos.y] = 42;
- }
- }
-
- // Use the hearth color to display power level
- if (engine->hearth_tile.isValid())
- {
- auto pos = engine->hearth_tile;
- int power = std::min(engine->max_power, get_steam_amount());
- 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, 3);
- df::coord dwater, dmagma;
- find_liquids(&dwater, &dmagma, engine->is_magma, 5);
-
- if (engine->water_tile.isValid())
- {
- if (!water.isValid())
- db->fore[engine->water_tile.x][engine->water_tile.y] = 0;
- else if (!dwater.isValid())
- db->bright[engine->water_tile.x][engine->water_tile.y] = 0;
- }
- if (engine->magma_tile.isValid() && engine->is_magma)
- {
- if (!magma.isValid())
- db->fore[engine->magma_tile.x][engine->magma_tile.y] = 0;
- else if (!dmagma.isValid())
- db->bright[engine->magma_tile.x][engine->magma_tile.y] = 0;
- }
- }
- }
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, deconstructItems, (bool noscatter, bool lost))
- {
- if (get_steam_engine())
- {
- // Explode if any steam left
- if (int amount = get_steam_amount())
- {
- make_explosion(
- df::coord((x1+x2)/2, (y1+y2)/2, z),
- 40 + amount * 20
- );
-
- random_boil();
- }
- }
-
- INTERPOSE_NEXT(deconstructItems)(noscatter, lost);
- }
-};
-
-IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, needsDesign);
-IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, getPowerInfo);
-IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, getMachineInfo);
-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);
-
-/*
- * Hook for the dwarfmode screen. Tweaks the build menu
- * behavior to suit the steam engine building more.
- */
-
-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 - cover channels with down stairs.";
- ui_build_selector->errors.push_back(new std::string(msg));
- }
- }
-
- DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *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. Since these workshops
- // are machines, they will collapse over true open space.
- check_hanging_tiles(get_steam_engine());
- }
-};
-
-IMPLEMENT_VMETHOD_INTERPOSE(dwarfmode_hook, feed);
-
-/*
- * Scan raws for matching workshop buildings.
- */
-
-static bool find_engines()
-{
- engines.clear();
-
- auto &wslist = world->raws.buildings.workshops;
-
- for (size_t i = 0; i < wslist.size(); i++)
- {
- if (strstr(wslist[i]->code.c_str(), "STEAM_ENGINE") == NULL)
- continue;
-
- steam_engine_workshop ws;
- ws.def = wslist[i];
- ws.id = ws.def->id;
-
- int bs = ws.def->build_stages;
- for (int x = 0; x < ws.def->dim_x; x++)
- {
- for (int y = 0; y < ws.def->dim_y; y++)
- {
- switch (ws.def->tile[bs][x][y])
- {
- case 15:
- ws.gear_tiles.push_back(df::coord2d(x,y));
- break;
- case 19:
- ws.hearth_tile = df::coord2d(x,y);
- break;
- }
-
- if (ws.def->tile_color[2][bs][x][y])
- {
- switch (ws.def->tile_color[0][bs][x][y])
- {
- case 1:
- ws.water_tile = df::coord2d(x,y);
- break;
- case 4:
- ws.magma_tile = df::coord2d(x,y);
- break;
- }
- }
- }
- }
-
- ws.is_magma = ws.def->needs_magma;
- ws.max_power = ws.is_magma ? 5 : 3;
- ws.max_capacity = ws.is_magma ? 10 : 6;
- ws.wear_temp = ws.is_magma ? 12000 : 11000;
-
- if (!ws.gear_tiles.empty())
- engines.push_back(ws);
- }
-
- return !engines.empty();
-}
-
-static void enable_hooks(bool enable)
-{
- INTERPOSE_HOOK(liquid_hook, getItemDescription).apply(enable);
- INTERPOSE_HOOK(liquid_hook, adjustTemperature).apply(enable);
- INTERPOSE_HOOK(liquid_hook, checkTemperatureDamage).apply(enable);
-
- INTERPOSE_HOOK(workshop_hook, needsDesign).apply(enable);
- INTERPOSE_HOOK(workshop_hook, getPowerInfo).apply(enable);
- INTERPOSE_HOOK(workshop_hook, getMachineInfo).apply(enable);
- INTERPOSE_HOOK(workshop_hook, isPowerSource).apply(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)
-{
- switch (event) {
- case SC_MAP_LOADED:
- if (find_engines())
- {
- out.print("Detected steam engine workshops - enabling plugin.\n");
- enable_hooks(true);
- }
- else
- enable_hooks(false);
- break;
- case SC_MAP_UNLOADED:
- enable_hooks(false);
- engines.clear();
- break;
- default:
- break;
- }
-
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
-{
- if (Core::getInstance().isMapLoaded())
- plugin_onstatechange(out, SC_MAP_LOADED);
-
- return CR_OK;
-}
-
-DFhackCExport command_result plugin_shutdown ( color_ostream &out )
-{
- enable_hooks(false);
- return CR_OK;
-}