diff --git a/docs/changelog.txt b/docs/changelog.txt index 4dae12dbfc..7a1b7075a8 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -115,6 +115,7 @@ Template for new versions: - Added ``Maps::describeSurroundings`` to get surroundings classification from savagery and evilness - Added ``Maps::getCurrentWeather`` to determine the current weather on the map. - Added ``Maps::forEachTile`` with ``Maps::TileFilter``/``Maps::TileActions`` to scan a cuboid of tiles, matching tiles by tiletype, tiletype attributes, and designation/occupancy bits, and counting, rewriting tiletypes, editing designation/occupancy bits, spawning constructions, or invoking a callback on matching tiles +- Added ``MapExtras::MapCache::propagateVerticalFlags`` to update the light/outside/subterranean flags of tiles below a tile that changed between an open and a solid shape - Added ``MiscUtils::print_range`` to print ranges with prefix, separator, and suffix - Added ``Units::breathes`` and ``Units::getBreathingState`` - Deprecated ``World::ReadCurrentWeather`` in favor of ``Maps::getCurrentWeather`` diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index c4502cc4b9..7428536afc 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -433,6 +433,11 @@ class DFHACK_EXPORT MapCache return b ? b->Allocate() : false; } + /// Propagate the light, outside, and subterranean flags of the tile at + /// pos to the tiles below it. Call after the tile at pos changed + /// between an open and a solid shape. + void propagateVerticalFlags(DFCoord pos); + /// delete the block from memory void discardBlock(Block *block); diff --git a/library/modules/MapCache.cpp b/library/modules/MapCache.cpp index a14d81288e..8f8f2eb4c7 100644 --- a/library/modules/MapCache.cpp +++ b/library/modules/MapCache.cpp @@ -1412,6 +1412,50 @@ void MapExtras::MapCache::discardBlock(Block *block) delete block; } +void MapExtras::MapCache::propagateVerticalFlags(DFCoord pos) +{ + df::tiletype_shape shape = tileShape(tiletypeAt(pos)); + bool open = shape == tiletype_shape::EMPTY || shape == tiletype_shape::RAMP_TOP; + + df::tile_designation td = designationAt(pos); + if (open) + { + if (!ensureBlockAt(DFCoord(pos.x, pos.y, pos.z+1))) { + // only the sky above + td.bits.light = true; + td.bits.outside = true; + td.bits.subterranean = false; + } + } + else + { + // a solid tile covers the column below; subterranean is a property + // of each tile and is preserved + td.bits.light = false; + td.bits.outside = false; + } + + for (int32_t z = pos.z - 1; + z >= 0 && ensureBlockAt(DFCoord(pos.x, pos.y, z)); + --z) + { + DFCoord pos_below(pos.x, pos.y, z); + df::tile_designation td_below = designationAt(pos_below); + bool sub = open ? td.bits.subterranean : td_below.bits.subterranean; + if (td_below.bits.light == td.bits.light + && td_below.bits.outside == td.bits.outside + && td_below.bits.subterranean == sub) + break; + td_below.bits.light = td.bits.light; + td_below.bits.outside = td.bits.outside; + td_below.bits.subterranean = sub; + setDesignationAt(pos_below, td_below); + shape = tileShape(tiletypeAt(pos_below)); + if (shape != tiletype_shape::EMPTY && shape != tiletype_shape::RAMP_TOP) + break; + } +} + void MapExtras::MapCache::resetTags() { for (auto it = blocks.begin(); it != blocks.end(); ++it)