From d5ea05ebb88b40483b62aaf5dfe20e1b24e8a28a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 6 Sep 2012 12:37:29 +0400 Subject: [PATCH 1/4] Implement a pressure plate sensitive to machine power. When built next to a gearbox, it will monitor its powered state. --- dfhack.init-example | 3 + library/lua/dfhack.lua | 2 +- library/lua/gui.lua | 8 +- library/lua/gui/dialogs.lua | 6 +- library/modules/Gui.cpp | 9 +- plugins/CMakeLists.txt | 1 + plugins/lua/power-meter.lua | 11 ++ plugins/power-meter.cpp | 237 ++++++++++++++++++++++++++++++++++++ scripts/gui/power-meter.lua | 116 ++++++++++++++++++ 9 files changed, 383 insertions(+), 10 deletions(-) create mode 100644 plugins/lua/power-meter.lua create mode 100644 plugins/power-meter.cpp create mode 100644 scripts/gui/power-meter.lua diff --git a/dfhack.init-example b/dfhack.init-example index af8b17f0f..5af527099 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 a1e899761..e96bb0f4b 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 23904c14f..f9b6ab6d2 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 35720f871..c4f15c9ac 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 1ea4bf687..8de908734 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 04da3e6c8..9093a493f 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 000000000..310e51c4e --- /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 000000000..0466b68e4 --- /dev/null +++ b/plugins/power-meter.cpp @@ -0,0 +1,237 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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 &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 000000000..8baf43e7e --- /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() From d0e630d4c35717bad682894e33e7dd57f86ac126 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 6 Sep 2012 17:10:58 +0400 Subject: [PATCH 2/4] Move steam engine out of devel, since it should be fully functional. --- plugins/CMakeLists.txt | 4 ++++ plugins/devel/CMakeLists.txt | 1 - plugins/{devel => raw}/building_steam_engine.txt | 0 plugins/{devel => raw}/item_trapcomp_steam_engine.txt | 0 plugins/{devel => raw}/reaction_steam_engine.txt | 0 plugins/{devel => }/steam-engine.cpp | 0 6 files changed, 4 insertions(+), 1 deletion(-) rename plugins/{devel => raw}/building_steam_engine.txt (100%) rename plugins/{devel => raw}/item_trapcomp_steam_engine.txt (100%) rename plugins/{devel => raw}/reaction_steam_engine.txt (100%) rename plugins/{devel => }/steam-engine.cpp (100%) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9093a493f..6e207385e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -44,6 +44,9 @@ endif() install(DIRECTORY lua/ DESTINATION ${DFHACK_LUA_DESTINATION}/plugins FILES_MATCHING PATTERN "*.lua") +install(DIRECTORY raw/ + DESTINATION ${DFHACK_DATA_DESTINATION}/raw + FILES_MATCHING PATTERN "*.txt") # Protobuf FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) @@ -114,6 +117,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(steam-engine steam-engine.cpp) DFHACK_PLUGIN(power-meter power-meter.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index f126ae53b..134d5cb67 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -18,7 +18,6 @@ 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_steam_engine.txt b/plugins/raw/building_steam_engine.txt similarity index 100% rename from plugins/devel/building_steam_engine.txt rename to plugins/raw/building_steam_engine.txt diff --git a/plugins/devel/item_trapcomp_steam_engine.txt b/plugins/raw/item_trapcomp_steam_engine.txt similarity index 100% rename from plugins/devel/item_trapcomp_steam_engine.txt rename to plugins/raw/item_trapcomp_steam_engine.txt diff --git a/plugins/devel/reaction_steam_engine.txt b/plugins/raw/reaction_steam_engine.txt similarity index 100% rename from plugins/devel/reaction_steam_engine.txt rename to plugins/raw/reaction_steam_engine.txt diff --git a/plugins/devel/steam-engine.cpp b/plugins/steam-engine.cpp similarity index 100% rename from plugins/devel/steam-engine.cpp rename to plugins/steam-engine.cpp From c971a819def1c5cc29dc926f62456c336a1dfa17 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 6 Sep 2012 22:45:19 +0400 Subject: [PATCH 3/4] Experimental creation of map blocks in gui/liquids script. --- LUA_API.rst | 4 +++ Lua API.html | 3 ++ library/LuaApi.cpp | 8 +++++ library/include/modules/MapCache.h | 10 ++++++ library/include/modules/Maps.h | 2 ++ library/modules/Maps.cpp | 58 +++++++++++++++++++++++++++++- library/xml | 2 +- scripts/gui/liquids.lua | 41 ++++++++++++++++++++- 8 files changed, 125 insertions(+), 3 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 799f623eb..d13c1e52e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -995,6 +995,10 @@ Maps module Returns a map block object for given df::coord or x,y,z in local tile coordinates. +* ``dfhack.maps.ensureTileBlock(coords)``, or ``ensureTileBlock(x,y,z)`` + + Like ``getTileBlock``, but if the block is not allocated, try creating it. + * ``dfhack.maps.getRegionBiome(region_coord2d)``, or ``getRegionBiome(x,y)`` Returns the biome info struct for the given global map region. diff --git a/Lua API.html b/Lua API.html index f05ee5511..546d27403 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1207,6 +1207,9 @@ Returns false in case of error.

  • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

    Returns a map block object for given df::coord or x,y,z in local tile coordinates.

  • +
  • dfhack.maps.ensureTileBlock(coords), or ensureTileBlock(x,y,z)

    +

    Like getTileBlock, but if the block is not allocated, try creating it.

    +
  • dfhack.maps.getRegionBiome(region_coord2d), or getRegionBiome(x,y)

    Returns the biome info struct for the given global map region.

  • diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1dcb001f1..63838d356 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -935,6 +935,13 @@ static int maps_getTileBlock(lua_State *L) return 1; } +static int maps_ensureTileBlock(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + Lua::PushDFObject(L, Maps::ensureTileBlock(pos)); + return 1; +} + static int maps_getRegionBiome(lua_State *L) { auto pos = CheckCoordXY(L, 1, true); @@ -951,6 +958,7 @@ static int maps_getTileBiomeRgn(lua_State *L) static const luaL_Reg dfhack_maps_funcs[] = { { "isValidTilePos", maps_isValidTilePos }, { "getTileBlock", maps_getTileBlock }, + { "ensureTileBlock", maps_ensureTileBlock }, { "getRegionBiome", maps_getRegionBiome }, { "getTileBiomeRgn", maps_getTileBiomeRgn }, { NULL, NULL } diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 109a20a41..262e70bbf 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -253,6 +253,8 @@ public: bool is_valid() { return valid; } df::map_block *getRaw() { return block; } + bool Allocate(); + MapCache *getParent() { return parent; } private: @@ -262,6 +264,8 @@ private: MapCache *parent; df::map_block *block; + void init(); + int biomeIndexAt(df::coord2d p); bool valid; @@ -347,6 +351,12 @@ class DFHACK_EXPORT MapCache return BlockAt(df::coord(coord.x>>4,coord.y>>4,coord.z)); } + bool ensureBlockAt(df::coord coord) + { + Block *b = BlockAtTile(coord); + return b ? b->Allocate() : false; + } + df::tiletype baseTiletypeAt (DFCoord tilecoord) { Block *b = BlockAtTile(tilecoord); diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 984cf16cf..869b21580 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -241,9 +241,11 @@ inline bool isValidTilePos(df::coord pos) { return isValidTilePos(pos.x, pos.y, */ extern DFHACK_EXPORT df::map_block * getBlock (int32_t blockx, int32_t blocky, int32_t blockz); extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t z); +extern DFHACK_EXPORT df::map_block * ensureTileBlock (int32_t x, int32_t y, int32_t z); inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); } inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); } +inline df::map_block * ensureTileBlock (df::coord pos) { return ensureTileBlock(pos.x, pos.y, pos.z); } extern DFHACK_EXPORT df::tiletype *getTileType(int32_t x, int32_t y, int32_t z); extern DFHACK_EXPORT df::tile_designation *getTileDesignation(int32_t x, int32_t y, int32_t z); diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 305f1296d..d0401164a 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -157,6 +157,39 @@ df::map_block *Maps::getTileBlock (int32_t x, int32_t y, int32_t z) return world->map.block_index[x >> 4][y >> 4][z]; } +df::map_block *Maps::ensureTileBlock (int32_t x, int32_t y, int32_t z) +{ + if (!isValidTilePos(x,y,z)) + return NULL; + + auto column = world->map.block_index[x >> 4][y >> 4]; + auto &slot = column[z]; + if (slot) + return slot; + + // Find another block below + int z2 = z; + while (z2 >= 0 && !column[z2]) z2--; + if (z2 < 0) + return NULL; + + slot = new df::map_block(); + slot->region_pos = column[z2]->region_pos; + slot->map_pos = column[z2]->map_pos; + slot->map_pos.z = z; + + // Assume sky + df::tile_designation dsgn(0); + dsgn.bits.light = true; + dsgn.bits.outside = true; + + for (int tx = 0; tx < 16; tx++) + for (int ty = 0; ty < 16; ty++) + slot->designation[tx][ty] = dsgn; + + return slot; +} + df::tiletype *Maps::getTileType(int32_t x, int32_t y, int32_t z) { df::map_block *block = getTileBlock(x,y,z); @@ -513,8 +546,14 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) valid = false; bcoord = _bcoord; block = Maps::getBlock(bcoord); - item_counts = NULL; tags = NULL; + + init(); +} + +void MapExtras::Block::init() +{ + item_counts = NULL; tiles = NULL; basemats = NULL; @@ -537,6 +576,23 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) } } +bool MapExtras::Block::Allocate() +{ + if (block) + return true; + + block = Maps::ensureTileBlock(bcoord.x*16, bcoord.y*16, bcoord.z); + if (!block) + return false; + + delete item_counts; + delete tiles; + delete basemats; + init(); + + return true; +} + MapExtras::Block::~Block() { delete[] item_counts; diff --git a/library/xml b/library/xml index df8178a98..a914f3b75 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit df8178a989373ec7868d9195d82ae5f85145ef81 +Subproject commit a914f3b7558335d53c0ac93f6e7267906a33cd29 diff --git a/scripts/gui/liquids.lua b/scripts/gui/liquids.lua index 869cac908..89f08b7cf 100644 --- a/scripts/gui/liquids.lua +++ b/scripts/gui/liquids.lua @@ -3,6 +3,7 @@ local utils = require 'utils' local gui = require 'gui' local guidm = require 'gui.dwarfmode' +local dlg = require 'gui.dialogs' local liquids = require('plugins.liquids') @@ -199,6 +200,42 @@ function LiquidsUI:onRenderBody(dc) dc:string("Enter", COLOR_LIGHTGREEN):string(": Paint") end +function ensure_blocks(cursor, size, cb) + local cx,cy,cz = pos2xyz(cursor) + local all = true + for x=1,size.x or 1,16 do + for y=1,size.y or 1,16 do + for z=1,size.z do + if not dfhack.maps.getTileBlock(cx+x-1, cy+y-1, cz+z-1) then + all = false + end + end + end + end + if all then + cb() + return + end + dlg.showYesNoPrompt( + 'Instantiate Blocks', + 'Not all map blocks are allocated - instantiate?\n\nWarning: new untested feature.', + COLOR_YELLOW, + function() + for x=1,size.x or 1,16 do + for y=1,size.y or 1,16 do + for z=1,size.z do + dfhack.maps.ensureTileBlock(cx+x-1, cy+y-1, cz+z-1) + end + end + end + cb() + end, + function() + cb() + end + ) +end + function LiquidsUI:onInput(keys) local paint = self.paint:get() local liquid = paint.liquid @@ -239,13 +276,15 @@ function LiquidsUI:onInput(keys) else guidm.clearSelection() end - liquids.paint( + local cb = curry( + liquids.paint, cursor, self.brush:get().tag, self.paint:get().tag, self.amount, size, self.set:get().tag, self.flow:get().tag, self.permaflow:get().tag ) + ensure_blocks(cursor, size, cb) elseif self:propagateMoveKeys(keys) then return elseif keys.D_LOOK_ARENA_WATER then From e925d8f4d999817d3ccf7f3180e7abee382e03b4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 7 Sep 2012 11:36:45 +0400 Subject: [PATCH 4/4] Add an API function for reading tiles from the screen buffers. --- LUA_API.rst | 5 ++++ Lua API.html | 4 ++++ library/LuaApi.cpp | 40 ++++++++++++++++++++++++++++++++ library/include/modules/Screen.h | 6 +++++ library/modules/Screen.cpp | 39 +++++++++++++++++++++++++++++-- 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index d13c1e52e..4d9170d6e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1308,6 +1308,11 @@ Basic painting functions: Returns *false* if coordinates out of bounds, or other error. +* ``dfhack.screen.readTile(x,y)`` + + Retrieves the contents of the specified tile from the screen buffers. + Returns a pen, or *nil* if invalid or TrueType. + * ``dfhack.screen.paintString(pen,x,y,text)`` Paints the string starting at *x,y*. Uses the string characters diff --git a/Lua API.html b/Lua API.html index 546d27403..dc9c8d73e 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1480,6 +1480,10 @@ Otherwise should be true/false.

    Returns false if coordinates out of bounds, or other error.

    +
  • dfhack.screen.readTile(x,y)

    +

    Retrieves the contents of the specified tile from the screen buffers. +Returns a pen, or nil if invalid or TrueType.

    +
  • dfhack.screen.paintString(pen,x,y,text)

    Paints the string starting at x,y. Uses the string characters in sequence to override the ch field of pen.

    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 63838d356..807cbf539 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1154,6 +1154,45 @@ static int screen_paintTile(lua_State *L) return 1; } +static int screen_readTile(lua_State *L) +{ + int x = luaL_checkint(L, 1); + int y = luaL_checkint(L, 2); + Pen pen = Screen::readTile(x, y); + + if (!pen.valid()) + { + lua_pushnil(L); + } + else + { + lua_newtable(L); + lua_pushinteger(L, pen.ch); lua_setfield(L, -2, "ch"); + lua_pushinteger(L, pen.fg); lua_setfield(L, -2, "fg"); + lua_pushinteger(L, pen.bg); lua_setfield(L, -2, "bg"); + lua_pushboolean(L, pen.bold); lua_setfield(L, -2, "bold"); + + if (pen.tile) + { + lua_pushinteger(L, pen.tile); lua_setfield(L, -2, "tile"); + + switch (pen.tile_mode) { + case Pen::CharColor: + lua_pushboolean(L, true); lua_setfield(L, -2, "tile_color"); + break; + case Pen::TileColor: + lua_pushinteger(L, pen.tile_fg); lua_setfield(L, -2, "tile_fg"); + lua_pushinteger(L, pen.tile_bg); lua_setfield(L, -2, "tile_bg"); + break; + default: + break; + } + } + } + + return 1; +} + static int screen_paintString(lua_State *L) { Pen pen; @@ -1258,6 +1297,7 @@ static const luaL_Reg dfhack_screen_funcs[] = { { "getMousePos", screen_getMousePos }, { "getWindowSize", screen_getWindowSize }, { "paintTile", screen_paintTile }, + { "readTile", screen_readTile }, { "paintString", screen_paintString }, { "fillRect", screen_fillRect }, { "findGraphicsTile", screen_findGraphicsTile }, diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 492e1eecc..4f47205f2 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -65,6 +65,9 @@ namespace DFHack } tile_mode; int8_t tile_fg, tile_bg; + bool valid() const { return tile >= 0; } + bool empty() const { return ch == 0 && tile == 0; } + Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) @@ -92,6 +95,9 @@ namespace DFHack /// Paint one screen tile with the given pen DFHACK_EXPORT bool paintTile(const Pen &pen, int x, int y); + /// Retrieves one screen tile from the buffer + DFHACK_EXPORT Pen readTile(int x, int y); + /// Paint a string onto the screen. Ignores ch and tile of pen. DFHACK_EXPORT bool paintString(const Pen &pen, int x, int y, const std::string &text); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index c2377f2ca..9f258fe02 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -100,7 +100,7 @@ static void doSetTile(const Pen &pen, int index) bool Screen::paintTile(const Pen &pen, int x, int y) { - if (!gps) return false; + if (!gps || !pen.valid()) return false; int dimx = gps->dimx, dimy = gps->dimy; if (x < 0 || x >= dimx || y < 0 || y >= dimy) return false; @@ -109,6 +109,41 @@ bool Screen::paintTile(const Pen &pen, int x, int y) return true; } +Pen Screen::readTile(int x, int y) +{ + if (!gps) return Pen(0,0,0,-1); + + int dimx = gps->dimx, dimy = gps->dimy; + if (x < 0 || x >= dimx || y < 0 || y >= dimy) + return Pen(0,0,0,-1); + + int index = x*dimy + y; + auto screen = gps->screen + index*4; + if (screen[3] & 0x80) + return Pen(0,0,0,-1); + + Pen pen( + screen[0], screen[1], screen[2], screen[3]?true:false, + gps->screentexpos[index] + ); + + if (pen.tile) + { + if (gps->screentexpos_grayscale[index]) + { + pen.tile_mode = Screen::Pen::TileColor; + pen.tile_fg = gps->screentexpos_cf[index]; + pen.tile_bg = gps->screentexpos_cbr[index]; + } + else if (gps->screentexpos_addcolor[index]) + { + pen.tile_mode = Screen::Pen::CharColor; + } + } + + return pen; +} + bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text) { if (!gps || y < 0 || y >= gps->dimy) return false; @@ -132,7 +167,7 @@ bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text) bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2) { - if (!gps) return false; + if (!gps || !pen.valid()) return false; if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0;