summaryrefslogtreecommitdiff
path: root/plugins/devel
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-09-01 11:29:05 +0400
committerAlexander Gavrilov2012-09-01 11:29:05 +0400
commitf158e1894d15c2d835f8ee8b65ce3b094b71e5f6 (patch)
tree8fcd6ad5065282687094a09edbfe8664e1e78cee /plugins/devel
parente0097d8d43013d72d729e2ae71cdd6a73a110d8d (diff)
downloaddfhack-f158e1894d15c2d835f8ee8b65ce3b094b71e5f6.tar.gz
dfhack-f158e1894d15c2d835f8ee8b65ce3b094b71e5f6.tar.bz2
dfhack-f158e1894d15c2d835f8ee8b65ce3b094b71e5f6.tar.xz
Further work on steam engine.
- Display water inside as 'boiling' by hooking item_liquid_miscst. - Store current power in flags to avoid mess if items disappear etc. - Suspend/unsuspend stoke jobs depending on steam level. - Implement intelligent steam use rate and boiler capacity cap. - Modify appearance of special tiles to display status.
Diffstat (limited to 'plugins/devel')
-rw-r--r--plugins/devel/building_zsteam_engine.txt2
-rw-r--r--plugins/devel/reaction_zsteam_engine.txt3
-rw-r--r--plugins/devel/steam-engine.cpp244
3 files changed, 192 insertions, 57 deletions
diff --git a/plugins/devel/building_zsteam_engine.txt b/plugins/devel/building_zsteam_engine.txt
index f76b237d..572eb407 100644
--- a/plugins/devel/building_zsteam_engine.txt
+++ b/plugins/devel/building_zsteam_engine.txt
@@ -30,9 +30,11 @@ building_zsteam_engine
[COLOR:2:1:6:0:0:0:0:0:7:0:0]
[COLOR:2:2:7:0:0:0:0:0:6:0:0]
[COLOR:2:3:7:0:0:MAT:7:0:0]
+ Tile 15 marks places where machines can connect:
[TILE:3:1:15:246:15]
[TILE:3:2:'\':19:'/']
[TILE:3:3:7:' ':7]
+ Color 0:?:1 marks hearth, 1:?:1 water indicator, 4:?:1 magma indicator:
[COLOR:3:1:6:0:0:6:0:0:6:0:0]
[COLOR:3:2:6:7:0:0:0:1:6:7:0]
[COLOR:3:3:1:7:1:0:0:0:4:7:1]
diff --git a/plugins/devel/reaction_zsteam_engine.txt b/plugins/devel/reaction_zsteam_engine.txt
index b8267cf5..1018510f 100644
--- a/plugins/devel/reaction_zsteam_engine.txt
+++ b/plugins/devel/reaction_zsteam_engine.txt
@@ -7,6 +7,7 @@ reaction_other
[BUILDING:STEAM_ENGINE:CUSTOM_S]
[BUILDING:MAGMA_STEAM_ENGINE:CUSTOM_S]
[FUEL]
- [PRODUCT:100:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:333]
[SKILL:SMELT]
+ Dimension is the number of days it can produce 100 power * 100.
+ [PRODUCT:100:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:1500]
diff --git a/plugins/devel/steam-engine.cpp b/plugins/devel/steam-engine.cpp
index 23af1217..5a26fb24 100644
--- a/plugins/devel/steam-engine.cpp
+++ b/plugins/devel/steam-engine.cpp
@@ -22,6 +22,8 @@
#include "df/world.h"
#include "df/buildings_other_id.h"
#include "df/machine.h"
+#include "df/job.h"
+#include "df/building_drawbuffer.h"
#include "MiscUtils.h"
@@ -40,6 +42,8 @@ DFHACK_PLUGIN("steam-engine");
struct steam_engine_workshop {
int id;
df::building_def_workshopst *def;
+ bool is_magma;
+ int max_power, max_capacity;
std::vector<df::coord2d> gear_tiles;
df::coord2d hearth_tile;
df::coord2d water_tile;
@@ -48,42 +52,94 @@ struct steam_engine_workshop {
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;
+}
+
+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 }
+};
+
+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);
+ }
+};
+
+IMPLEMENT_VMETHOD_INTERPOSE(liquid_hook, getItemDescription);
+
struct workshop_hook : df::building_workshopst {
typedef df::building_workshopst interpose_base;
steam_engine_workshop *get_steam_engine()
{
if (type == workshop_type::Custom)
- for (size_t i = 0; i < engines.size(); i++)
- if (engines[i].id == custom_type)
- return &engines[i];
+ return find_steam_engine(custom_type);
return NULL;
}
+ // Use high bits of flags to store current steam amount.
+ // This is necessary for consistency if items disappear unexpectedly.
+
int get_steam_amount()
{
- int cnt = 0;
+ return (flags.whole >> 28) & 15;
+ }
- for (size_t i = 0; i < contained_items.size(); i++)
- {
- if (contained_items[i]->use_mode == 0 &&
- contained_items[i]->item->flags.bits.in_building)
- cnt++;
- }
+ void set_steam_amount(int count)
+ {
+ flags.whole = (flags.whole & 0x0FFFFFFFU) | uint32_t((count & 15) << 28);
+ }
+
+ void absorb_unit(steam_engine_workshop *engine, df::item_liquid_miscst *liquid)
+ {
+ liquid->flags.bits.in_building = true;
+ liquid->mat_state.whole |= liquid_hook::BOILING_FLAG;
- return cnt;
+ // 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);
}
- int get_power_output(steam_engine_workshop *engine)
+ bool boil_unit(df::item_liquid_miscst *liquid)
{
- int maxv = engine->def->needs_magma ? 5 : 3;
- return std::min(get_steam_amount(), maxv)*100;
+ 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;
}
- df::item_liquid_miscst *collect_steam()
+ 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--)
{
@@ -98,19 +154,54 @@ struct workshop_hook : df::building_workshopst {
if (!liquid->flags.bits.in_building)
{
if (liquid->mat_type != builtin_mats::WATER ||
- liquid->dimension != 333 ||
+ liquid->age > 1 ||
liquid->wear != 0)
continue;
- liquid->flags.bits.in_building = true;
+ absorb_unit(engine, liquid);
}
- first = liquid;
+ if (*count < engine->max_capacity)
+ {
+ first = liquid;
+ ++*count;
+ }
+ else
+ {
+ // Overpressure valve
+ boil_unit(liquid);
+ }
}
return first;
}
+ static const int WEAR_TICKS = 806400;
+
+ 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;
+ }
+ // apply rate; 10% steam is wasted anyway
+ ticks *= (0.1f + 0.9f*power_rate)*power_level;
+ // end result
+ return std::max(1, int(ticks));
+ }
+
+ // Furnaces need architecture, and this is a workshop
+ // only because furnaces cannot connect to machines.
DEFINE_VMETHOD_INTERPOSE(bool, needsDesign, ())
{
if (get_steam_engine())
@@ -119,11 +210,12 @@ struct workshop_hook : df::building_workshopst {
return INTERPOSE_NEXT(needsDesign)();
}
+ // Machine interface
DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info))
{
if (auto engine = get_steam_engine())
{
- info->produced = get_power_output(engine);
+ info->produced = std::min(engine->max_power, get_steam_amount())*100;
info->consumed = 10;
return;
}
@@ -178,6 +270,7 @@ struct workshop_hook : df::building_workshopst {
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;
@@ -199,37 +292,76 @@ struct workshop_hook : df::building_workshopst {
{
if (auto engine = get_steam_engine())
{
- int output = get_power_output(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())
+ if (auto first = collect_steam(engine, &cur_count))
{
- if (first->incWearTimer(output))
+ int rate = get_steam_use_rate(engine, first->dimension, old_power);
+
+ if (first->incWearTimer(rate))
{
- while (first->wear_timer >= 806400)
+ while (first->wear_timer >= WEAR_TICKS)
{
- first->wear_timer -= 806400;
+ first->wear_timer -= WEAR_TICKS;
first->wear++;
}
if (first->wear > 3)
{
- first->flags.bits.in_building = 0;
- first->temperature = first->getBoilingPoint()+50;
+ boil_unit(first);
+ cur_count--;
}
}
}
- int new_out = get_power_output(engine);
- if (new_out != output)
+ 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 += (new_out - output);
+ mptr->cur_power += (cur_power - old_power)*100;
}
}
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 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];
+ }
+ }
+ }
};
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, needsDesign);
@@ -240,8 +372,9 @@ IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, categorize);
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, uncategorize);
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, canConnectToMachine);
IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, updateAction);
+IMPLEMENT_VMETHOD_INTERPOSE(workshop_hook, drawBuilding);
-static void find_engines()
+static bool find_engines()
{
engines.clear();
@@ -284,47 +417,46 @@ static void find_engines()
}
}
- engines.push_back(ws);
+ ws.is_magma = ws.def->needs_magma;
+ ws.max_power = ws.is_magma ? 5 : 3;
+ ws.max_capacity = ws.is_magma ? 10 : 6;
+
+ if (!ws.gear_tiles.empty())
+ engines.push_back(ws);
}
-}
-static void enable_hooks()
-{
- INTERPOSE_HOOK(workshop_hook, needsDesign).apply();
- INTERPOSE_HOOK(workshop_hook, getPowerInfo).apply();
- INTERPOSE_HOOK(workshop_hook, getMachineInfo).apply();
- INTERPOSE_HOOK(workshop_hook, isPowerSource).apply();
- INTERPOSE_HOOK(workshop_hook, categorize).apply();
- INTERPOSE_HOOK(workshop_hook, uncategorize).apply();
- INTERPOSE_HOOK(workshop_hook, canConnectToMachine).apply();
- INTERPOSE_HOOK(workshop_hook, updateAction).apply();
+ return !engines.empty();
}
-static void disable_hooks()
+static void enable_hooks(bool enable)
{
- INTERPOSE_HOOK(workshop_hook, needsDesign).remove();
- INTERPOSE_HOOK(workshop_hook, getPowerInfo).remove();
- INTERPOSE_HOOK(workshop_hook, getMachineInfo).remove();
- INTERPOSE_HOOK(workshop_hook, isPowerSource).remove();
- INTERPOSE_HOOK(workshop_hook, categorize).remove();
- INTERPOSE_HOOK(workshop_hook, uncategorize).remove();
- INTERPOSE_HOOK(workshop_hook, canConnectToMachine).remove();
- INTERPOSE_HOOK(workshop_hook, updateAction).remove();
+ INTERPOSE_HOOK(liquid_hook, getItemDescription).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, updateAction).apply(enable);
+ INTERPOSE_HOOK(workshop_hook, drawBuilding).apply(enable);
}
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event) {
case SC_MAP_LOADED:
- find_engines();
- if (!engines.empty())
+ if (find_engines())
{
out.print("Detected steam engine workshops - enabling plugin.\n");
- enable_hooks();
+ enable_hooks(true);
}
+ else
+ enable_hooks(false);
break;
case SC_MAP_UNLOADED:
- disable_hooks();
+ enable_hooks(false);
engines.clear();
break;
default:
@@ -344,6 +476,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
- disable_hooks();
+ enable_hooks(false);
return CR_OK;
}