Merge pull request #2880 from Alberth289346/refactor-th-map-cpp

Refactor th map cpp
This commit is contained in:
Stephen E. Baker
2025-05-30 21:38:21 -04:00
committed by GitHub
5 changed files with 187 additions and 149 deletions

View File

@@ -79,8 +79,8 @@ SOFTWARE.
#endif
/** Standard includes **/
#include <cstddef> // IWYU pragma: export
#include <cstdint> // IWYU pragma: export
#include <cstddef> // IWYU pragma: export
#include <cstdint> // IWYU pragma: export
// We bring in the most common stddef and stdint types to avoid typing
// clang-format off

View File

@@ -259,7 +259,7 @@ int l_map_updateblueprint(lua_State* L) {
for (int iY = iOldY; iY < iOldY + iOldH; ++iY) {
for (int iX = iOldX; iX < iOldX + iOldW; ++iX) {
map_tile* pNode = pMap->get_tile_unchecked(iX, iY);
pNode->iBlock[3] = 0;
pNode->tile_layers[tile_layer::ui] = 0;
pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint;
pNode->flags.passable_if_not_for_blueprint = false;
}
@@ -270,9 +270,9 @@ int l_map_updateblueprint(lua_State* L) {
for (int iX = iNewX; iX < iNewX + iNewW; ++iX) {
map_tile* pNode = pMap->get_tile_unchecked(iX, iY);
if (is_valid(entire_invalid, pNode, pMap, player_id)) {
pNode->iBlock[3] = iFloorTileGood;
pNode->tile_layers[tile_layer::ui] = iFloorTileGood;
} else {
pNode->iBlock[3] = iFloorTileBad;
pNode->tile_layers[tile_layer::ui] = iFloorTileBad;
valid = false;
}
pNode->flags.passable_if_not_for_blueprint = pNode->flags.passable;
@@ -285,20 +285,20 @@ int l_map_updateblueprint(lua_State* L) {
int iCenterY = iNewY + (iNewH - 2) / 2;
map_tile* pNode = pMap->get_tile_unchecked(iCenterX, iCenterY);
if (pNode->iBlock[3] == iFloorTileGood)
pNode->iBlock[3] = iFloorTileGoodCenter + 2;
if (pNode->tile_layers[tile_layer::ui] == iFloorTileGood)
pNode->tile_layers[tile_layer::ui] = iFloorTileGoodCenter + 2;
pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY);
if (pNode->iBlock[3] == iFloorTileGood)
pNode->iBlock[3] = iFloorTileGoodCenter + 1;
if (pNode->tile_layers[tile_layer::ui] == iFloorTileGood)
pNode->tile_layers[tile_layer::ui] = iFloorTileGoodCenter + 1;
pNode = pMap->get_tile_unchecked(iCenterX, iCenterY + 1);
if (pNode->iBlock[3] == iFloorTileGood)
pNode->iBlock[3] = iFloorTileGoodCenter + 0;
if (pNode->tile_layers[tile_layer::ui] == iFloorTileGood)
pNode->tile_layers[tile_layer::ui] = iFloorTileGoodCenter + 0;
pNode = pMap->get_tile_unchecked(iCenterX + 1, iCenterY + 1);
if (pNode->iBlock[3] == iFloorTileGood)
pNode->iBlock[3] = iFloorTileGoodCenter + 3;
if (pNode->tile_layers[tile_layer::ui] == iFloorTileGood)
pNode->tile_layers[tile_layer::ui] = iFloorTileGoodCenter + 3;
}
// Set wall animations
@@ -467,16 +467,16 @@ int l_map_getcell(lua_State* L) {
iX + 1, iY + 1));
}
if (lua_isnoneornil(L, 4)) {
lua_pushinteger(L, pNode->iBlock[0]);
lua_pushinteger(L, pNode->iBlock[1]);
lua_pushinteger(L, pNode->iBlock[2]);
lua_pushinteger(L, pNode->iBlock[3]);
lua_pushinteger(L, pNode->tile_layers[tile_layer::ground]);
lua_pushinteger(L, pNode->tile_layers[tile_layer::north_wall]);
lua_pushinteger(L, pNode->tile_layers[tile_layer::west_wall]);
lua_pushinteger(L, pNode->tile_layers[tile_layer::ui]);
return 4;
} else {
lua_Integer iLayer = luaL_checkinteger(L, 4) - 1;
if (iLayer < 0 || iLayer >= 4)
return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)");
lua_pushinteger(L, pNode->iBlock[iLayer]);
if (iLayer < tile_layer::ground || iLayer >= tile_layer::num_tile_layers)
return luaL_argerror(L, 4, "Tile layer index is out of bounds (1-4)");
lua_pushinteger(L, pNode->tile_layers[iLayer]);
return 1;
}
}
@@ -658,16 +658,20 @@ int l_map_setcell(lua_State* L) {
return luaL_argerror(L, 2, "Map coordinates out of bounds");
}
if (lua_gettop(L) >= 7) {
pNode->iBlock[0] = (uint16_t)luaL_checkinteger(L, 4);
pNode->iBlock[1] = (uint16_t)luaL_checkinteger(L, 5);
pNode->iBlock[2] = (uint16_t)luaL_checkinteger(L, 6);
pNode->iBlock[3] = (uint16_t)luaL_checkinteger(L, 7);
pNode->tile_layers[tile_layer::ground] =
static_cast<uint16_t>(luaL_checkinteger(L, 4));
pNode->tile_layers[tile_layer::north_wall] =
static_cast<uint16_t>(luaL_checkinteger(L, 5));
pNode->tile_layers[tile_layer::west_wall] =
static_cast<uint16_t>(luaL_checkinteger(L, 6));
pNode->tile_layers[tile_layer::ui] =
static_cast<uint16_t>(luaL_checkinteger(L, 7));
} else {
lua_Integer iLayer = luaL_checkinteger(L, 4) - 1;
if (iLayer < 0 || iLayer >= 4)
return luaL_argerror(L, 4, "Layer index is out of bounds (1-4)");
uint16_t iBlock = static_cast<uint16_t>(luaL_checkinteger(L, 5));
pNode->iBlock[iLayer] = iBlock;
lua_Integer layer = luaL_checkinteger(L, 4) - 1;
if (layer < tile_layer::ground || layer >= tile_layer::num_tile_layers)
return luaL_argerror(L, 4, "Tile layer index is out of bounds (1-4)");
uint16_t blk = static_cast<uint16_t>(luaL_checkinteger(L, 5));
pNode->tile_layers[layer] = blk;
}
lua_settop(L, 1);
@@ -705,8 +709,8 @@ int l_map_mark_room(lua_State* L) {
for (int iY = iY_; iY < iY_ + iH; ++iY) {
for (int iX = iX_; iX < iX_ + iW; ++iX) {
map_tile* pNode = pMap->get_tile_unchecked(iX, iY);
pNode->iBlock[0] = iTile;
pNode->iBlock[3] = 0;
pNode->tile_layers[tile_layer::ground] = iTile;
pNode->tile_layers[tile_layer::ui] = 0;
pNode->flags.room = true;
pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint;
pNode->flags.passable_if_not_for_blueprint = false;
@@ -735,7 +739,9 @@ int l_map_unmark_room(lua_State* L) {
for (int iY = iY_; iY < iY_ + iH; ++iY) {
for (int iX = iX_; iX < iX_ + iW; ++iX) {
map_tile* pNode = pMap->get_tile_unchecked(iX, iY);
pNode->iBlock[0] = pMap->get_original_tile_unchecked(iX, iY)->iBlock[0];
pNode->tile_layers[tile_layer::ground] =
pMap->get_original_tile_unchecked(iX, iY)
->tile_layers[tile_layer::ground];
pNode->flags.room = false;
pNode->iRoomId = 0;
}

View File

@@ -48,13 +48,13 @@ uint8_t range_scale(uint16_t low, uint16_t high, uint16_t val, uint16_t start,
}
inline bool is_wall(uint16_t blk) {
return ((82 <= ((blk) & 0xFF)) && (((blk) & 0xFF) <= 164));
return 82 <= (blk & 0xFF) && (blk & 0xFF) <= 164;
}
inline bool is_wall_drawn(const level_map& map, const map_tile& node,
const map_tile& original_node, size_t n) {
return map.get_tile_owner(&node) != 0 ? is_wall(node.iBlock[n])
: is_wall(original_node.iBlock[n]);
const map_tile& original_node, tile_layer n) {
return map.get_tile_owner(&node) != 0 ? is_wall(node.tile_layers[n])
: is_wall(original_node.tile_layers[n]);
}
int l_town_map_draw(lua_State* L) {
@@ -133,7 +133,8 @@ int l_town_map_draw(lua_State* L) {
pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3);
}
dont_paint_tile:
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 1)) {
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode,
tile_layer::north_wall)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1);
// Draw entrance door
@@ -146,7 +147,7 @@ int l_town_map_draw(lua_State* L) {
}
}
}
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, 2)) {
if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, tile_layer::west_wall)) {
pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3);
// Draw entrance door

View File

@@ -248,17 +248,17 @@ map_tile_flags::operator uint32_t() const {
}
map_tile::map_tile() : iParcelId(0), iRoomId(0), flags({}), objects() {
iBlock[0] = 0;
iBlock[1] = 0;
iBlock[2] = 0;
iBlock[3] = 0;
tile_layers[tile_layer::ground] = 0;
tile_layers[tile_layer::north_wall] = 0;
tile_layers[tile_layer::west_wall] = 0;
tile_layers[tile_layer::ui] = 0;
aiTemperature[0] = aiTemperature[1] = 8192;
}
level_map::level_map()
: cells(nullptr),
original_cells(nullptr),
blocks(nullptr),
wall_blocks(nullptr),
overlay(nullptr),
owns_overlay(false),
plot_owner(nullptr),
@@ -381,7 +381,8 @@ bool level_map::load_blank() {
map_tile* pOriginalNode = original_cells;
for (int iY = 0; iY < height; ++iY) {
for (int iX = 0; iX < width; ++iX, ++pNode, ++pOriginalNode) {
pNode->iBlock[0] = static_cast<uint16_t>(2 + (iX % 2));
pNode->tile_layers[tile_layer::ground] =
static_cast<uint16_t>(2 + (iX % 2));
}
}
plot_owner = new int[1];
@@ -450,28 +451,29 @@ bool level_map::load_from_th_file(const uint8_t* pData, size_t iDataLength,
pNode->flags.can_travel_s = false;
}
pNode->iBlock[0] = iBaseTile;
pNode->tile_layers[tile_layer::ground] = iBaseTile;
if (pData[3] == 0 || is_divider_wall(pData[3])) {
// Tiles 71, 72 and 73 (pond foliage) are used as floor tiles,
// but are too tall to be floor tiles, so move them to a wall,
// and replace the floor with something similar (pond base).
if (71 <= iBaseTile && iBaseTile <= 73) {
pNode->iBlock[1] = iBaseTile;
pNode->iBlock[0] = 69;
pNode->tile_layers[tile_layer::north_wall] = iBaseTile;
pNode->tile_layers[tile_layer::ground] = 69;
} else {
pNode->iBlock[1] = 0;
pNode->tile_layers[tile_layer::north_wall] = 0;
}
} else {
pNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]];
pNode->tile_layers[tile_layer::north_wall] =
gs_iTHMapBlockLUT[pData[3]];
pNode->flags.can_travel_n = false;
if (iY != 0) {
pNode[-this->width].flags.can_travel_s = false;
}
}
if (pData[4] == 0 || is_divider_wall(pData[4])) {
pNode->iBlock[2] = 0;
pNode->tile_layers[tile_layer::west_wall] = 0;
} else {
pNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]];
pNode->tile_layers[tile_layer::west_wall] = gs_iTHMapBlockLUT[pData[4]];
pNode->flags.can_travel_w = false;
if (iX != 0) {
pNode[-1].flags.can_travel_e = false;
@@ -508,10 +510,12 @@ bool level_map::load_from_th_file(const uint8_t* pData, size_t iDataLength,
*pOriginalNode = *pNode;
if (is_divider_wall(pData[3])) {
pOriginalNode->iBlock[1] = gs_iTHMapBlockLUT[pData[3]];
pOriginalNode->tile_layers[tile_layer::north_wall] =
gs_iTHMapBlockLUT[pData[3]];
}
if (is_divider_wall(pData[4])) {
pOriginalNode->iBlock[2] = gs_iTHMapBlockLUT[pData[4]];
pOriginalNode->tile_layers[tile_layer::west_wall] =
gs_iTHMapBlockLUT[pData[4]];
}
if (pData[1] != 0 && fnObjectCallback != nullptr) {
@@ -568,9 +572,12 @@ void level_map::save(const std::string& filename) {
: pNode->objects.front());
// Blocks
aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[0] & 0xFF];
aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[1] & 0xFF];
aBuffer[iBufferNext++] = aReverseBlockLUT[pNode->iBlock[2] & 0xFF];
aBuffer[iBufferNext++] =
aReverseBlockLUT[pNode->tile_layers[tile_layer::ground] & 0xFF];
aBuffer[iBufferNext++] =
aReverseBlockLUT[pNode->tile_layers[tile_layer::north_wall] & 0xFF];
aBuffer[iBufferNext++] =
aReverseBlockLUT[pNode->tile_layers[tile_layer::west_wall] & 0xFF];
// Flags (TODO: Set a few more flag bits?)
uint8_t iFlags = 63;
@@ -650,17 +657,17 @@ namespace {
*/
bool addRemoveDividerWalls(level_map* pMap, map_tile* pNode,
const map_tile* pOriginalNode, int iXY, int delta,
int block, int iParcelId) {
tile_layer layer, int iParcelId) {
if (iXY > 0 && pOriginalNode->flags.hospital &&
pOriginalNode[-delta].flags.hospital &&
pNode->iParcelId != pNode[-delta].iParcelId) {
int iOwner = pMap->get_parcel_owner(pNode->iParcelId);
int iOtherOwner = pMap->get_parcel_owner(pNode[-delta].iParcelId);
if (iOwner != iOtherOwner) {
pNode->iBlock[block] = block + (iOwner ? 143 : 141);
pNode->tile_layers[layer] = layer + (iOwner ? 143 : 141);
} else if (pNode->iParcelId == iParcelId ||
pNode[-delta].iParcelId == iParcelId) {
pNode->iBlock[block] = 0;
pNode->tile_layers[layer] = 0;
return true;
}
}
@@ -684,31 +691,36 @@ std::vector<std::pair<int, int>> level_map::set_parcel_owner(int iParcelId,
for (int iX = 0; iX < this->width; ++iX, ++pNode, ++pOriginalNode) {
if (pNode->iParcelId == iParcelId) {
if (iOwner != 0) {
pNode->iBlock[0] = pOriginalNode->iBlock[0];
pNode->iBlock[1] = pOriginalNode->iBlock[1];
pNode->iBlock[2] = pOriginalNode->iBlock[2];
pNode->tile_layers[tile_layer::ground] =
pOriginalNode->tile_layers[tile_layer::ground];
pNode->tile_layers[tile_layer::north_wall] =
pOriginalNode->tile_layers[tile_layer::north_wall];
pNode->tile_layers[tile_layer::west_wall] =
pOriginalNode->tile_layers[tile_layer::west_wall];
pNode->flags = pOriginalNode->flags;
} else {
// Nicely mown grass pattern
pNode->iBlock[0] = static_cast<uint16_t>(((iX & 1) << 1) + 1);
pNode->tile_layers[tile_layer::ground] =
static_cast<uint16_t>(((iX & 1) << 1) + 1);
pNode->iBlock[1] = 0;
pNode->iBlock[2] = 0;
pNode->tile_layers[tile_layer::north_wall] = 0;
pNode->tile_layers[tile_layer::west_wall] = 0;
pNode->flags = {};
// Random decoration
if (((iX | iY) & 0x7) == 0) {
int iWhich = (iX ^ iY) % 9;
pNode->iBlock[1] = static_cast<uint16_t>(192 + iWhich);
pNode->tile_layers[tile_layer::north_wall] =
static_cast<uint16_t>(192 + iWhich);
}
}
}
if (addRemoveDividerWalls(this, pNode, pOriginalNode, iX, 1, 2,
iParcelId)) {
if (addRemoveDividerWalls(this, pNode, pOriginalNode, iX, 1,
tile_layer::west_wall, iParcelId)) {
vSplitTiles.push_back(std::make_pair(iX, iY));
}
if (addRemoveDividerWalls(this, pNode, pOriginalNode, iY, this->width, 1,
iParcelId)) {
if (addRemoveDividerWalls(this, pNode, pOriginalNode, iY, this->width,
tile_layer::north_wall, iParcelId)) {
vSplitTiles.push_back(std::make_pair(iX, iY));
}
}
@@ -937,16 +949,16 @@ const map_tile* level_map::get_original_tile_unchecked(int iX, int iY) const {
return original_cells + iY * width + iX;
}
void level_map::set_block_sheet(sprite_sheet* pSheet) { blocks = pSheet; }
void level_map::set_block_sheet(sprite_sheet* pSheet) { wall_blocks = pSheet; }
void level_map::set_all_wall_draw_flags(uint8_t iFlags) {
uint16_t iBlockOr = static_cast<uint16_t>(iFlags << 8);
uint16_t draw_flags = static_cast<uint16_t>(iFlags << 8);
map_tile* pNode = cells;
for (int i = 0; i < width * height; ++i, ++pNode) {
pNode->iBlock[1] =
static_cast<uint16_t>((pNode->iBlock[1] & 0xFF) | iBlockOr);
pNode->iBlock[2] =
static_cast<uint16_t>((pNode->iBlock[2] & 0xFF) | iBlockOr);
pNode->tile_layers[tile_layer::north_wall] = static_cast<uint16_t>(
(pNode->tile_layers[tile_layer::north_wall] & 0xFF) | draw_flags);
pNode->tile_layers[tile_layer::west_wall] = static_cast<uint16_t>(
(pNode->tile_layers[tile_layer::west_wall] & 0xFF) | draw_flags);
}
}
@@ -972,7 +984,7 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
2) For each tile, left to right, the west wall, then the late entities
*/
if (blocks == nullptr || cells == nullptr) {
if (wall_blocks == nullptr || cells == nullptr) {
return;
}
@@ -987,26 +999,26 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
itrNode1; ++itrNode1) {
// First, draw the floor tile as it should be below everything else.
int iH = 32;
unsigned int iBlock = itrNode1->iBlock[0];
blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH);
blocks->draw_sprite(
pCanvas, iBlock & 0xFF,
uint16_t layer = itrNode1->tile_layers[tile_layer::ground];
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH);
wall_blocks->draw_sprite(
pCanvas, layer & 0xFF,
itrNode1.tile_x_position_on_screen() + iCanvasX - 32,
itrNode1.tile_y_position_on_screen() + iCanvasY - iH + 32,
(iBlock >> 8) | thdf_nearest);
(layer >> 8) | thdf_nearest);
// Draw floor shadows immediately after floor tiles ensuring that all
// shadow pixels are drawn onto freshly drawn opaque floor tile pixels.
if (itrNode1->flags.shadow_full) {
blocks->draw_sprite(pCanvas, 74,
itrNode1.tile_x_position_on_screen() + iCanvasX - 32,
itrNode1.tile_y_position_on_screen() + iCanvasY,
thdf_alpha_75 | thdf_nearest);
wall_blocks->draw_sprite(
pCanvas, 74, itrNode1.tile_x_position_on_screen() + iCanvasX - 32,
itrNode1.tile_y_position_on_screen() + iCanvasY,
thdf_alpha_75 | thdf_nearest);
} else if (itrNode1->flags.shadow_half) {
blocks->draw_sprite(pCanvas, 75,
itrNode1.tile_x_position_on_screen() + iCanvasX - 32,
itrNode1.tile_y_position_on_screen() + iCanvasY,
thdf_alpha_75 | thdf_nearest);
wall_blocks->draw_sprite(
pCanvas, 75, itrNode1.tile_x_position_on_screen() + iCanvasX - 32,
itrNode1.tile_y_position_on_screen() + iCanvasY,
thdf_alpha_75 | thdf_nearest);
}
}
@@ -1023,12 +1035,12 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
iCanvasY);
itrNode; ++itrNode) {
int iH;
unsigned int iBlock = itrNode->iBlock[1];
if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) &&
iH > 0) {
blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(iBlock >> 8) | thdf_nearest);
uint16_t layer = itrNode->tile_layers[tile_layer::north_wall];
if (layer != 0 &&
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH) && iH > 0) {
wall_blocks->draw_sprite(pCanvas, layer & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(layer >> 8) | thdf_nearest);
if (itrNode->flags.shadow_wall) {
clip_rect rcNewClip;
rcNewClip.x = static_cast<clip_rect::x_y_type>(itrNode.x() - 32);
@@ -1037,8 +1049,9 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
rcNewClip.w = static_cast<clip_rect::w_h_type>(64);
rcNewClip.h = static_cast<clip_rect::w_h_type>(86 - 4);
render_target::scoped_clip clip(pCanvas, &rcNewClip);
blocks->draw_sprite(pCanvas, 156, itrNode.x() - 32, itrNode.y() - 56,
thdf_alpha_75 | thdf_nearest);
wall_blocks->draw_sprite(pCanvas, 156, itrNode.x() - 32,
itrNode.y() - 56,
thdf_alpha_75 | thdf_nearest);
}
}
drawable* pItem = static_cast<drawable*>(itrNode->oEarlyEntities.next);
@@ -1065,23 +1078,23 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
for (; itrNode; ++itrNode) {
bool bNeedsRedraw = false;
int iH;
unsigned int iBlock = itrNode->iBlock[2];
if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) &&
iH > 0) {
blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(iBlock >> 8) | thdf_nearest);
uint16_t layer = itrNode->tile_layers[tile_layer::west_wall];
if (layer != 0 &&
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH) && iH > 0) {
wall_blocks->draw_sprite(pCanvas, layer & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(layer >> 8) | thdf_nearest);
}
iBlock = itrNode->iBlock[3];
if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) &&
iH > 0) {
blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(iBlock >> 8) | thdf_nearest);
layer = itrNode->tile_layers[tile_layer::ui];
if (layer != 0 &&
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH) && iH > 0) {
wall_blocks->draw_sprite(pCanvas, layer & 0xFF, itrNode.x() - 32,
itrNode.y() - iH + 32,
(layer >> 8) | thdf_nearest);
}
iBlock = itrNode->iBlock[1];
if (iBlock != 0 && blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) &&
iH > 0) {
layer = itrNode->tile_layers[tile_layer::north_wall];
if (layer != 0 &&
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH) && iH > 0) {
bNeedsRedraw = true;
}
if (itrNode->oEarlyEntities.next) {
@@ -1140,12 +1153,14 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
// north side or a wall to the north redraw that tile
if (bTileNeedsRedraw) {
// redraw the north wall
unsigned int iBlock = itrNode.get_previous_tile()->iBlock[1];
if (iBlock != 0 &&
blocks->get_sprite_size(iBlock & 0xFF, nullptr, &iH) && iH > 0) {
blocks->draw_sprite(pCanvas, iBlock & 0xFF, itrNode.x() - 96,
itrNode.y() - iH + 32,
(iBlock >> 8) | thdf_nearest);
uint16_t layer =
itrNode.get_previous_tile()->tile_layers[tile_layer::north_wall];
if (layer != 0 &&
wall_blocks->get_sprite_size(layer & 0xFF, nullptr, &iH) &&
iH > 0) {
wall_blocks->draw_sprite(pCanvas, layer & 0xFF, itrNode.x() - 96,
itrNode.y() - iH + 32,
(layer >> 8) | thdf_nearest);
if (itrNode.get_previous_tile()->flags.shadow_wall) {
clip_rect rcNewClip;
rcNewClip.x = static_cast<clip_rect::x_y_type>(itrNode.x() - 96);
@@ -1154,9 +1169,9 @@ void level_map::draw(render_target* pCanvas, int iScreenX, int iScreenY,
rcNewClip.w = static_cast<clip_rect::w_h_type>(64);
rcNewClip.h = static_cast<clip_rect::w_h_type>(86 - 4);
render_target::scoped_clip clip(pCanvas, &rcNewClip);
blocks->draw_sprite(pCanvas, 156, itrNode.x() - 96,
itrNode.y() - 56,
thdf_alpha_75 | thdf_nearest);
wall_blocks->draw_sprite(pCanvas, 156, itrNode.x() - 96,
itrNode.y() - 56,
thdf_alpha_75 | thdf_nearest);
}
}
@@ -1198,7 +1213,7 @@ drawable* level_map::hit_test(int iTestX, int iTestY) const {
// This function needs to hitTest each drawable object, in the reverse
// order to that in which they would be drawn.
if (blocks == nullptr || cells == nullptr) {
if (wall_blocks == nullptr || cells == nullptr) {
return nullptr;
}
@@ -1408,13 +1423,13 @@ void level_map::update_pathfinding() {
pNode->flags.can_travel_s = false;
}
if (pNode->iBlock[1] & 0xFF) {
if (pNode->tile_layers[tile_layer::north_wall] & 0xFF) {
pNode->flags.can_travel_n = false;
if (iY != 0) {
pNode[-this->width].flags.can_travel_s = false;
}
}
if (pNode->iBlock[2] & 0xFF) {
if (pNode->tile_layers[tile_layer::west_wall] & 0xFF) {
pNode->flags.can_travel_w = false;
if (iX != 0) {
pNode[-1].flags.can_travel_e = false;
@@ -1429,9 +1444,9 @@ namespace {
//! For shadow casting, a tile is considered to have a wall on a direction
//! if it has a door in that direction, or the block is from the hardcoded
//! range of wall-like blocks.
bool is_wall(map_tile* tile, size_t block, bool flag) {
return flag || (82 <= (tile->iBlock[block] & 0xFF) &&
(tile->iBlock[block] & 0xFF) <= 164);
bool is_wall(map_tile* tile, tile_layer layer, bool flag) {
return flag || (82 <= (tile->tile_layers[layer] & 0xFF) &&
(tile->tile_layers[layer] & 0xFF) <= 164);
}
} // namespace
@@ -1443,14 +1458,15 @@ void level_map::update_shadows() {
pNode->flags.shadow_full = false;
pNode->flags.shadow_half = false;
pNode->flags.shadow_wall = false;
if (is_wall(pNode, 2, pNode->flags.tall_west)) {
if (is_wall(pNode, tile_layer::west_wall, pNode->flags.tall_west)) {
pNode->flags.shadow_half = true;
if (is_wall(pNode, 1, pNode->flags.tall_north)) {
if (is_wall(pNode, tile_layer::north_wall, pNode->flags.tall_north)) {
pNode->flags.shadow_wall = true;
} else if (iY != 0) {
map_tile* pNeighbour = pNode - this->width;
pNeighbour->flags.shadow_full = true;
if (iX != 0 && !is_wall(pNeighbour, 2, pNode->flags.tall_west)) {
if (iX != 0 && !is_wall(pNeighbour, tile_layer::west_wall,
pNode->flags.tall_west)) {
// Wrap the shadow around a corner (no need to continue
// all the way along the wall, as the shadow would be
// occluded by the wall. If Debug->Transparent Walls is
@@ -1491,10 +1507,10 @@ void level_map::persist(lua_persist_writer* pWriter) const {
oEncoder.initialise(6);
for (map_tile *pNode = cells, *pLimitNode = cells + width * height;
pNode != pLimitNode; ++pNode) {
oEncoder.write(pNode->iBlock[0]);
oEncoder.write(pNode->iBlock[1]);
oEncoder.write(pNode->iBlock[2]);
oEncoder.write(pNode->iBlock[3]);
oEncoder.write(pNode->tile_layers[tile_layer::ground]);
oEncoder.write(pNode->tile_layers[tile_layer::north_wall]);
oEncoder.write(pNode->tile_layers[tile_layer::west_wall]);
oEncoder.write(pNode->tile_layers[tile_layer::ui]);
oEncoder.write(pNode->iParcelId);
oEncoder.write(pNode->iRoomId);
// Flags include THOB values, and other things which do not work
@@ -1520,9 +1536,9 @@ void level_map::persist(lua_persist_writer* pWriter) const {
for (map_tile *pNode = original_cells,
*pLimitNode = original_cells + width * height;
pNode != pLimitNode; ++pNode) {
oEncoder.write(pNode->iBlock[0]);
oEncoder.write(pNode->iBlock[1]);
oEncoder.write(pNode->iBlock[2]);
oEncoder.write(pNode->tile_layers[tile_layer::ground]);
oEncoder.write(pNode->tile_layers[tile_layer::north_wall]);
oEncoder.write(pNode->tile_layers[tile_layer::west_wall]);
oEncoder.write(pNode->iParcelId);
oEncoder.write(static_cast<uint32_t>(pNode->flags));
}
@@ -1626,10 +1642,13 @@ void level_map::depersist(lua_persist_reader* pReader) {
oDecoder.initialise(6, pReader);
for (map_tile *pNode = cells, *pLimitNode = cells + width * height;
pNode != pLimitNode; ++pNode) {
pNode->iBlock[0] = static_cast<uint16_t>(oDecoder.read());
pNode->iBlock[1] = static_cast<uint16_t>(oDecoder.read());
pNode->iBlock[2] = static_cast<uint16_t>(oDecoder.read());
pNode->iBlock[3] = static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::ground] =
static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::north_wall] =
static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::west_wall] =
static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::ui] = static_cast<uint16_t>(oDecoder.read());
pNode->iParcelId = static_cast<uint16_t>(oDecoder.read());
pNode->iRoomId = static_cast<uint16_t>(oDecoder.read());
}
@@ -1637,9 +1656,12 @@ void level_map::depersist(lua_persist_reader* pReader) {
for (map_tile *pNode = original_cells,
*pLimitNode = original_cells + width * height;
pNode != pLimitNode; ++pNode) {
pNode->iBlock[0] = static_cast<uint16_t>(oDecoder.read());
pNode->iBlock[1] = static_cast<uint16_t>(oDecoder.read());
pNode->iBlock[2] = static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::ground] =
static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::north_wall] =
static_cast<uint16_t>(oDecoder.read());
pNode->tile_layers[tile_layer::west_wall] =
static_cast<uint16_t>(oDecoder.read());
pNode->iParcelId = static_cast<uint16_t>(oDecoder.read());
pNode->flags = oDecoder.read();
}

View File

@@ -182,6 +182,15 @@ enum class temperature_theme {
yellow_red //!< Gradients of yellow, orange, and red
};
enum tile_layer : uint8_t {
ground = 0,
north_wall = 1,
west_wall = 2,
ui = 3,
num_tile_layers = 4 //!< Number of layers in a tile.
};
struct map_tile {
map_tile();
~map_tile() = default;
@@ -192,15 +201,15 @@ struct map_tile {
//! Linked list for entities rendered in an early (right-to-left) pass
link_list oEarlyEntities;
//! Block tiles for rendering
//! For each tile, the lower byte is the index in the sprite sheet, and the
//! Tile layers for rendering.
//! For each layer, the lower byte is the index in the sprite sheet, and the
//! upper byte is for the drawing flags.
//! Layer 0 is for the floor
//! Layer 1 is for the north wall
//! Layer 2 is for the west wall
//! Layer 3 is for the UI
//! NB: In Lua, layers are numbered 1 - 4 rather than 0 - 3
uint16_t iBlock[4];
uint16_t tile_layers[tile_layer::num_tile_layers];
//! Parcels (plots) of land have an ID, with each tile in the plot having
//! that ID. Parcel 0 is the outside.
@@ -420,7 +429,7 @@ class level_map {
map_tile* cells;
map_tile* original_cells; // Cells at map load time, before any changes
sprite_sheet* blocks;
sprite_sheet* wall_blocks;
map_overlay* overlay;
bool owns_overlay;
int* plot_owner; // 0 for unowned, 1 for player 1, etc.