diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index d1aed8979..9e78edd3e 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -324,7 +324,7 @@ df::building *Buildings::allocInstance(df::coord pos, df::building_type type, in { auto obj = (df::building_trapst*)bld; if (obj->trap_type == trap_type::PressurePlate) - obj->unk_cc = 500; + obj->ready_timeout = 500; break; } default: diff --git a/library/xml b/library/xml index 328a8dbdc..9b3ded158 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 328a8dbdc7d9e1e838798abf79861cc18a387e3f +Subproject commit 9b3ded15848e830784ef2dc4dea6093175669bc9 diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 134d5cb67..f126ae53b 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -18,6 +18,7 @@ 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_zsteam_engine.txt b/plugins/devel/building_zsteam_engine.txt new file mode 100644 index 000000000..f76b237d5 --- /dev/null +++ b/plugins/devel/building_zsteam_engine.txt @@ -0,0 +1,86 @@ +building_zsteam_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:MAT:0:0:0:7:0:0] + [COLOR:0:2:0:0:0:0:0:0:7:0:0] + [COLOR:0:3:6:0:0:0:0:0:0:0:0] + [TILE:1:1:246:128:' '] + [TILE:1:2:' ':' ':254] + [TILE:1:3:254:240:240] + [COLOR:1:1:6:0:0:7:0:0:0:0:0] + [COLOR:1:2:0:0:0:0:0:0:7:0:0] + [COLOR:1:3:7:0:0:MAT:MAT] + [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:6:0:0] + [COLOR:2:3:7:0:0:MAT:7:0:0] + [TILE:3:1:15:246:15] + [TILE:3:2:'\':19:'/'] + [TILE:3:3:7:' ':7] + [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] + [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:WEAPON:WEAPON_MACE: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:2: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:MAT:0:0:0:7:0:0] + [COLOR:0:2:0:0:0:0:0:0:7:0:0] + [COLOR:0:3:6:0:0:0:0:0:0:0:0] + [TILE:1:1:246:128:' '] + [TILE:1:2:' ':' ':254] + [TILE:1:3:254:240:240] + [COLOR:1:1:6:0:0:7:0:0:0:0:0] + [COLOR:1:2:0:0:0:0:0:0:7:0:0] + [COLOR:1:3:7:0:0:MAT:MAT] + [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:6:0:0] + [COLOR:2:3:7:0:0:MAT:7:0:0] + [TILE:3:1:15:246:15] + [TILE:3:2:'\':19:'/'] + [TILE:3:3:7:' ':7] + [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] + [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:WEAPON:WEAPON_MACE: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:2:BLOCKS:NONE:NONE:NONE][BUILDMAT][MAGMA_BUILD_SAFE] diff --git a/plugins/devel/reaction_zsteam_engine.txt b/plugins/devel/reaction_zsteam_engine.txt new file mode 100644 index 000000000..b8267cf55 --- /dev/null +++ b/plugins/devel/reaction_zsteam_engine.txt @@ -0,0 +1,12 @@ +reaction_other + +[OBJECT:REACTION] + +[REACTION:STOKE_BOILER] + [NAME:stoke the boiler] + [BUILDING:STEAM_ENGINE:CUSTOM_S] + [BUILDING:MAGMA_STEAM_ENGINE:CUSTOM_S] + [FUEL] + [PRODUCT:100:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:333] + [SKILL:SMELT] + diff --git a/plugins/devel/steam-engine.cpp b/plugins/devel/steam-engine.cpp new file mode 100644 index 000000000..23af12177 --- /dev/null +++ b/plugins/devel/steam-engine.cpp @@ -0,0 +1,349 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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 "MiscUtils.h" + +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_build_selector; + +DFHACK_PLUGIN("steam-engine"); + +struct steam_engine_workshop { + int id; + df::building_def_workshopst *def; + std::vector gear_tiles; + df::coord2d hearth_tile; + df::coord2d water_tile; + df::coord2d magma_tile; +}; + +std::vector engines; + +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 NULL; + } + + int get_steam_amount() + { + int cnt = 0; + + 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++; + } + + return cnt; + } + + int get_power_output(steam_engine_workshop *engine) + { + int maxv = engine->def->needs_magma ? 5 : 3; + return std::min(get_steam_amount(), maxv)*100; + } + + df::item_liquid_miscst *collect_steam() + { + df::item_liquid_miscst *first = NULL; + + 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(item->item); + if (!liquid) + continue; + + if (!liquid->flags.bits.in_building) + { + if (liquid->mat_type != builtin_mats::WATER || + liquid->dimension != 333 || + liquid->wear != 0) + continue; + + liquid->flags.bits.in_building = true; + } + + first = liquid; + } + + return first; + } + + DEFINE_VMETHOD_INTERPOSE(bool, needsDesign, ()) + { + if (get_steam_engine()) + return true; + + return INTERPOSE_NEXT(needsDesign)(); + } + + DEFINE_VMETHOD_INTERPOSE(void, getPowerInfo, (df::power_info *info)) + { + if (auto engine = get_steam_engine()) + { + info->produced = get_power_output(engine); + info->consumed = 10; + 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++) + { + 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); + } + + DEFINE_VMETHOD_INTERPOSE(void, updateAction, ()) + { + if (auto engine = get_steam_engine()) + { + int output = get_power_output(engine); + + if (auto first = collect_steam()) + { + if (first->incWearTimer(output)) + { + while (first->wear_timer >= 806400) + { + first->wear_timer -= 806400; + first->wear++; + } + + if (first->wear > 3) + { + first->flags.bits.in_building = 0; + first->temperature = first->getBoilingPoint()+50; + } + } + } + + int new_out = get_power_output(engine); + if (new_out != output) + { + auto mptr = df::machine::find(machine.machine_id); + if (mptr) + mptr->cur_power += (new_out - output); + } + } + + INTERPOSE_NEXT(updateAction)(); + } +}; + +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, updateAction); + +static void 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++) + { + if (ws.def->tile[bs][x][y] == 15) + ws.gear_tiles.push_back(df::coord2d(x,y)); + + if (ws.def->tile_color[2][bs][x][y]) + { + switch (ws.def->tile_color[0][bs][x][y]) + { + case 0: + ws.hearth_tile = df::coord2d(x,y); + break; + case 1: + ws.water_tile = df::coord2d(x,y); + break; + case 4: + ws.magma_tile = df::coord2d(x,y); + break; + default: + break; + } + } + } + } + + 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(); +} + +static void disable_hooks() +{ + 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(); +} + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_MAP_LOADED: + find_engines(); + if (!engines.empty()) + { + out.print("Detected steam engine workshops - enabling plugin.\n"); + enable_hooks(); + } + break; + case SC_MAP_UNLOADED: + disable_hooks(); + engines.clear(); + break; + default: + break; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + if (Core::getInstance().isMapLoaded()) + plugin_onstatechange(out, SC_MAP_LOADED); + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + disable_hooks(); + return CR_OK; +} diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 591125c5e..fa99f39e5 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -7,6 +7,7 @@ #include "PluginManager.h" #include "modules/Gui.h" +#include "modules/Screen.h" #include "modules/Units.h" #include "modules/Items.h" @@ -29,6 +30,8 @@ #include "df/unit_inventory_item.h" #include "df/viewscreen_dwarfmodest.h" #include "df/squad_order_trainst.h" +#include "df/ui_build_selector.h" +#include "df/building_trapst.h" #include @@ -40,6 +43,9 @@ using namespace df::enums; using df::global::ui; using df::global::world; +using df::global::ui_build_selector; +using df::global::ui_menu_width; +using df::global::ui_area_map_width; using namespace DFHack::Gui; @@ -77,6 +83,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector main.mode == ui_sidebar_mode::Build && + ui_build_selector->stage == 1 && + ui_build_selector->building_type == building_type::Trap && + ui_build_selector->building_subtype == trap_type::PressurePlate && + ui_build_selector->plate_info.flags.bits.units) + { + auto wsize = Screen::getWindowSize(); + int x = wsize.x - MENU_WIDTH - 1; + if (*ui_menu_width == 1 || *ui_area_map_width == 2) + x -= AREA_MAP_WIDTH + 1; + + Screen::Pen pen(' ',COLOR_WHITE); + + int minv = ui_build_selector->plate_info.unit_min; + if ((minv % 1000) == 0) + Screen::paintString(pen, x+11, 14, stl_sprintf("%3dK ", minv/1000)); + + int maxv = ui_build_selector->plate_info.unit_max; + if (maxv < 200000 && (maxv % 1000) == 0) + Screen::paintString(pen, x+24, 14, stl_sprintf("%3dK ", maxv/1000)); + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(readable_build_plate_hook, render); + +static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) +{ + if (vector_get(parameters, 1) == "disable") + { + hook.remove(); + out.print("Disabled tweak %s\n", parameters[0].c_str()); + } + else + { + if (hook.apply()) + out.print("Enabled tweak %s\n", parameters[0].c_str()); + else + out.printerr("Could not activate tweak %s\n", parameters[0].c_str()); + } +} + static command_result tweak(color_ostream &out, vector ¶meters) { CoreSuspender suspend; @@ -302,19 +364,21 @@ static command_result tweak(color_ostream &out, vector ¶meters) } else if (cmd == "stable-cursor") { - auto &hook = INTERPOSE_HOOK(stable_cursor_hook, feed); - if (vector_get(parameters, 1) == "disable") - hook.remove(); - else - hook.apply(); + enable_hook(out, INTERPOSE_HOOK(stable_cursor_hook, feed), parameters); } else if (cmd == "patrol-duty") { - auto &hook = INTERPOSE_HOOK(patrol_duty_hook, isPatrol); - if (vector_get(parameters, 1) == "disable") - hook.remove(); - else - hook.apply(); + enable_hook(out, INTERPOSE_HOOK(patrol_duty_hook, isPatrol), parameters); + } + else if (cmd == "readable-build-plate") + { + if (!ui_build_selector || !ui_menu_width || !ui_area_map_width) + { + out.printerr("Necessary globals not known.\n"); + return CR_FAILURE; + } + + enable_hook(out, INTERPOSE_HOOK(readable_build_plate_hook, render), parameters); } else return CR_WRONG_USAGE;