diff options
| author | Alexander Gavrilov | 2012-09-06 12:37:29 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-09-06 12:37:29 +0400 |
| commit | d5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a (patch) | |
| tree | d900522e6e8599029f5534e34fb744f0a595bb09 | |
| parent | 8d876cc7d92faf1616d914e03c890772256ebb83 (diff) | |
| download | dfhack-d5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a.tar.gz dfhack-d5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a.tar.bz2 dfhack-d5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a.tar.xz | |
Implement a pressure plate sensitive to machine power.
When built next to a gearbox, it will monitor its powered state.
| -rw-r--r-- | dfhack.init-example | 3 | ||||
| -rw-r--r-- | library/lua/dfhack.lua | 2 | ||||
| -rw-r--r-- | library/lua/gui.lua | 8 | ||||
| -rw-r--r-- | library/lua/gui/dialogs.lua | 6 | ||||
| -rw-r--r-- | library/modules/Gui.cpp | 9 | ||||
| -rw-r--r-- | plugins/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | plugins/lua/power-meter.lua | 11 | ||||
| -rw-r--r-- | plugins/power-meter.cpp | 237 | ||||
| -rw-r--r-- | scripts/gui/power-meter.lua | 116 |
9 files changed, 383 insertions, 10 deletions
diff --git a/dfhack.init-example b/dfhack.init-example index af8b17f0..5af52709 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -56,6 +56,9 @@ keybinding add Alt-R@dwarfmode/QueryBuilding/Some gui/room-list.work # interface for the liquids plugin keybinding add Alt-L@dwarfmode/LookAround gui/liquids +# machine power sensitive pressure plate construction +keybinding add Ctrl-Shift-M@dwarfmode/Build/Position/Trap gui/power-meter + ################### # UI logic tweaks # ################### diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index a1e89976..e96bb0f4 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -84,7 +84,7 @@ function mkmodule(module,env) error("Not a table in package.loaded["..module.."]") end end - local plugname = string.match(module,'^plugins%.(%w+)$') + local plugname = string.match(module,'^plugins%.([%w%-]+)$') if plugname then dfhack.open_plugin(pkg,plugname) end diff --git a/library/lua/gui.lua b/library/lua/gui.lua index 23904c14..f9b6ab6d 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -162,10 +162,10 @@ function Painter:fill(x1,y1,x2,y2,pen,bg,bold) if type(x1) == 'table' then x1, y1, x2, y2, pen, bg, bold = x1.x1, x1.y1, x1.x2, x1.y2, y1, x2, y2 end - x1 = math.max(x1,self.clip_x1) - y1 = math.max(y1,self.clip_y1) - x2 = math.min(x2,self.clip_x2) - y2 = math.min(y2,self.clip_y2) + x1 = math.max(x1+self.x1,self.clip_x1) + y1 = math.max(y1+self.y1,self.clip_y1) + x2 = math.min(x2+self.x1,self.clip_x2) + y2 = math.min(y2+self.y1,self.clip_y2) dscreen.fillRect(to_pen(self.cur_pen,pen,bg,bold),x1,y1,x2,y2) return self end diff --git a/library/lua/gui/dialogs.lua b/library/lua/gui/dialogs.lua index 35720f87..c4f15c9a 100644 --- a/library/lua/gui/dialogs.lua +++ b/library/lua/gui/dialogs.lua @@ -127,7 +127,7 @@ function InputBox:onRenderBody(dc) dc:newline(1) dc:pen(self.input_pen or COLOR_LIGHTCYAN) - dc:fill(dc.x1+1,dc.y,dc.x2-1,dc.y) + dc:fill(1,dc:localY(),dc.width-2,dc:localY()) local cursor = '_' if math.floor(dfhack.getTickCount()/300) % 2 == 0 then @@ -135,9 +135,7 @@ function InputBox:onRenderBody(dc) end local txt = self.input .. cursor if #txt > dc.width-2 then - txt = string.sub(txt, #txt-dc.width+3) - -- Add prefix arrow - dc:advance(-1):char(27) + txt = string.char(27)..string.sub(txt, #txt-dc.width+4) end dc:string(txt) end diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 1ea4bf68..8de90873 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -211,7 +211,14 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) if (ui_build_selector->building_type < 0) focus += "/Type"; else if (ui_build_selector->stage != 2) - focus += "/Position"; + { + if (ui_build_selector->stage != 1) + focus += "/NoMaterials"; + else + focus += "/Position"; + + focus += "/" + enum_item_key(ui_build_selector->building_type); + } else { focus += "/Material"; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 04da3e6c..9093a493 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -114,6 +114,7 @@ if (BUILD_SUPPORTED) # this one exports functions to lua DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) + DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) endif() diff --git a/plugins/lua/power-meter.lua b/plugins/lua/power-meter.lua new file mode 100644 index 00000000..310e51c4 --- /dev/null +++ b/plugins/lua/power-meter.lua @@ -0,0 +1,11 @@ +local _ENV = mkmodule('plugins.power-meter') + +--[[ + + Native functions: + + * makePowerMeter(plate_info,min_power,max_power,invert) + +--]] + +return _ENV
\ No newline at end of file diff --git a/plugins/power-meter.cpp b/plugins/power-meter.cpp new file mode 100644 index 00000000..0466b68e --- /dev/null +++ b/plugins/power-meter.cpp @@ -0,0 +1,237 @@ +#include "Core.h" +#include <Console.h> +#include <Export.h> +#include <Error.h> +#include <PluginManager.h> +#include <modules/Gui.h> +#include <modules/Screen.h> +#include <modules/Maps.h> +#include <modules/World.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_trapst.h" +#include "df/builtin_mats.h" +#include "df/world.h" +#include "df/buildings_other_id.h" +#include "df/machine.h" +#include "df/machine_info.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" + +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("power-meter"); + +static const uint32_t METER_BIT = 0x80000000U; + +static void init_plate_info(df::pressure_plate_info &plate_info) +{ + plate_info.water_min = 1; + plate_info.water_max = 7; + plate_info.flags.whole = METER_BIT; + plate_info.flags.bits.water = true; + plate_info.flags.bits.resets = true; +} + +/* + * Hook for the pressure plate itself. Implements core logic. + */ + +struct trap_hook : df::building_trapst { + typedef df::building_trapst interpose_base; + + // Engine detection + + bool is_power_meter() + { + return trap_type == trap_type::PressurePlate && + (plate_info.flags.whole & METER_BIT) != 0; + } + + inline bool is_fully_built() + { + return getBuildStage() >= getMaxBuildStage(); + } + + DEFINE_VMETHOD_INTERPOSE(void, getName, (std::string *buf)) + { + if (is_power_meter()) + { + buf->clear(); + *buf += "Power Meter"; + return; + } + + INTERPOSE_NEXT(getName)(buf); + } + + DEFINE_VMETHOD_INTERPOSE(void, updateAction, ()) + { + if (is_power_meter()) + { + auto pdsgn = Maps::getTileDesignation(centerx,centery,z); + + if (pdsgn) + { + bool active = false; + auto &gears = world->buildings.other[buildings_other_id::GEAR_ASSEMBLY]; + + for (size_t i = 0; i < gears.size(); i++) + { + // Adjacent + auto gear = gears[i]; + int deltaxy = abs(centerx - gear->centerx) + abs(centery - gear->centery); + if (gear->z != z || deltaxy != 1) + continue; + // Linked to machine + auto info = gears[i]->getMachineInfo(); + if (!info || info->machine_id < 0) + continue; + // an active machine + auto machine = df::machine::find(info->machine_id); + if (!machine || !machine->flags.bits.active) + continue; + // with adequate power? + int power = machine->cur_power - machine->min_power; + if (power < 0 || machine->cur_power <= 0) + continue; + if (power < plate_info.track_min) + continue; + if (power > plate_info.track_max && plate_info.track_max >= 0) + continue; + + active = true; + break; + } + + if (plate_info.flags.bits.citizens) + active = !active; + + // Temporarily set the tile water amount based on power state + auto old_dsgn = *pdsgn; + pdsgn->bits.liquid_type = tile_liquid::Water; + pdsgn->bits.flow_size = (active ? 7 : 0); + + INTERPOSE_NEXT(updateAction)(); + + *pdsgn = old_dsgn; + return; + } + } + + INTERPOSE_NEXT(updateAction)(); + } + + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk)) + { + INTERPOSE_NEXT(drawBuilding)(db, unk); + + if (is_power_meter() && is_fully_built()) + { + db->fore[0][0] = 3; + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, getName); +IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, updateAction); +IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, drawBuilding); + +static bool enabled = false; + +static void enable_hooks(bool enable) +{ + enabled = enable; + + INTERPOSE_HOOK(trap_hook, getName).apply(enable); + INTERPOSE_HOOK(trap_hook, updateAction).apply(enable); + INTERPOSE_HOOK(trap_hook, drawBuilding).apply(enable); +} + +static bool makePowerMeter(df::pressure_plate_info *info, int min_power, int max_power, bool invert) +{ + CHECK_NULL_POINTER(info); + + if (!enabled) + { + auto pworld = Core::getInstance().getWorld(); + auto entry = pworld->GetPersistentData("power-meter/enabled", NULL); + if (!entry.isValid()) + return false; + + enable_hooks(true); + } + + init_plate_info(*info); + info->track_min = min_power; + info->track_max = max_power; + info->flags.bits.citizens = invert; + return true; +} + +DFHACK_PLUGIN_LUA_FUNCTIONS { + DFHACK_LUA_FUNCTION(makePowerMeter), + DFHACK_LUA_END +}; + +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_MAP_LOADED: + { + auto pworld = Core::getInstance().getWorld(); + bool enable = pworld->GetPersistentData("power-meter/enabled").isValid(); + + if (enable) + { + out.print("Enabling the power meter plugin.\n"); + enable_hooks(true); + } + } + break; + case SC_MAP_UNLOADED: + enable_hooks(false); + 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; +} diff --git a/scripts/gui/power-meter.lua b/scripts/gui/power-meter.lua new file mode 100644 index 00000000..8baf43e7 --- /dev/null +++ b/scripts/gui/power-meter.lua @@ -0,0 +1,116 @@ +-- Interface front-end for power-meter plugin. + +local utils = require 'utils' +local gui = require 'gui' +local guidm = require 'gui.dwarfmode' +local dlg = require 'gui.dialogs' + +local plugin = require('plugins.power-meter') +local bselector = df.global.ui_build_selector + +PowerMeter = defclass(PowerMeter, guidm.MenuOverlay) + +PowerMeter.focus_path = 'power-meter' + +function PowerMeter:init() + self:init_fields{ + min_power = 0, max_power = -1, invert = false, + } + guidm.MenuOverlay.init(self) + return self +end + +function PowerMeter:onShow() + guidm.MenuOverlay.onShow(self) + + -- Send an event to update the errors + bselector.plate_info.flags.whole = 0 + self:sendInputToParent('BUILDING_TRIGGER_ENABLE_WATER') +end + +function PowerMeter:onRenderBody(dc) + dc:fill(0,0,dc.width-1,13,gui.CLEAR_PEN) + dc:seek(1,1):pen(COLOR_WHITE) + dc:string("Power Meter"):newline():newline(1) + dc:string("Placement"):newline():newline(1) + + dc:string("Excess power range:") + + dc:newline(3):string("as", COLOR_LIGHTGREEN) + dc:string(": Min ") + if self.min_power <= 0 then + dc:string("(any)") + else + dc:string(''..self.min_power) + end + + dc:newline(3):string("zx", COLOR_LIGHTGREEN) + dc:string(": Max ") + if self.max_power < 0 then + dc:string("(any)") + else + dc:string(''..self.max_power) + end + dc:newline():newline(1) + + dc:string("i",COLOR_LIGHTGREEN):string(": ") + if self.invert then + dc:string("Inverted") + else + dc:string("Not inverted") + end +end + +function PowerMeter:onInput(keys) + if keys.CUSTOM_I then + self.invert = not self.invert + elseif keys.BUILDING_TRIGGER_MIN_WATER_UP then + self.min_power = self.min_power + 10 + elseif keys.BUILDING_TRIGGER_MIN_WATER_DOWN then + self.min_power = math.max(0, self.min_power - 10) + elseif keys.BUILDING_TRIGGER_MAX_WATER_UP then + if self.max_power < 0 then + self.max_power = 0 + else + self.max_power = self.max_power + 10 + end + elseif keys.BUILDING_TRIGGER_MAX_WATER_DOWN then + self.max_power = math.max(-1, self.max_power - 10) + elseif keys.LEAVESCREEN then + self:dismiss() + self:sendInputToParent('LEAVESCREEN') + elseif keys.SELECT then + if #bselector.errors == 0 then + if not plugin.makePowerMeter( + bselector.plate_info, + self.min_power, self.max_power, self.invert + ) + then + dlg.showMessage( + 'Power Meter', + 'Could not initialize.', COLOR_LIGHTRED + ) + + self:dismiss() + self:sendInputToParent('LEAVESCREEN') + return + end + + self:sendInputToParent('SELECT') + if bselector.stage ~= 1 then + self:dismiss() + end + end + elseif self:propagateMoveKeys(keys) then + return + end +end + +if dfhack.gui.getCurFocus() ~= 'dwarfmode/Build/Position/Trap' +or bselector.building_subtype ~= df.trap_type.PressurePlate +then + qerror("This script requires the main dwarfmode view in build pressure plate mode") +end + +local list = mkinstance(PowerMeter):init() +list:show() |
