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