Implement a pressure plate sensitive to machine power.
When built next to a gearbox, it will monitor its powered state.develop
parent
8d876cc7d9
commit
d5ea05ebb8
@ -0,0 +1,11 @@
|
||||
local _ENV = mkmodule('plugins.power-meter')
|
||||
|
||||
--[[
|
||||
|
||||
Native functions:
|
||||
|
||||
* makePowerMeter(plate_info,min_power,max_power,invert)
|
||||
|
||||
--]]
|
||||
|
||||
return _ENV
|
@ -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;
|
||||
}
|
@ -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()
|
Loading…
Reference in New Issue