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