diff --git a/data/maps/desert.tmx b/data/maps/desert.tmx index a95218e..986240b 100644 --- a/data/maps/desert.tmx +++ b/data/maps/desert.tmx @@ -2,6 +2,7 @@ + eAHt2DsOgCAQQMG9/y3kpGhHyErHJ2EKE8VC3TdaWCKi2MyAAQYYYIABBhhggAEGGGCAAQYYYIABBjYZeN7r9pt/lvv+2/YtsmN91vbJGmRruqzrks1/tKbN/Daj+f+d00WXmw2078U3h/Y42795Vic8uybzv1cndHYPOjPAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAAD5xmocwy5Ow== diff --git a/data/maps/sample_dungeon.tmx b/data/maps/sample_dungeon.tmx index 9fc34f5..efb7fb8 100644 --- a/data/maps/sample_dungeon.tmx +++ b/data/maps/sample_dungeon.tmx @@ -2,6 +2,7 @@ + eAHt3TERAAAIA7HiFWv4RAYMGWqgl/0nyZgPGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhg4MBAl26CbgQDDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMDAPwMLiAQuiA== diff --git a/data/maps/template_forrest.tmx b/data/maps/template_forrest.tmx index 5d2b790..7125fb9 100644 --- a/data/maps/template_forrest.tmx +++ b/data/maps/template_forrest.tmx @@ -2,6 +2,7 @@ + eAHt2zEKACAIAEB7q/3/DfUAxwSDG1wcJOIGBc0VkcIfMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMXAObAw4YYIABBhhggIHvDVQ9XZVzY+zOmgEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBqYYsOPE4hSL3sEiAwwwwAADDDDAAAMMdBswAzPWbUx9xhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggIG3Bg4x3kMn diff --git a/data/maps/template_forrest2.tmx b/data/maps/template_forrest2.tmx index e627692..56756f1 100644 --- a/data/maps/template_forrest2.tmx +++ b/data/maps/template_forrest2.tmx @@ -2,6 +2,7 @@ + eAHt2zEKACAIAEB7q/3/DfUAxwSDG1wcJOIGBc0VkcIfMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMXAObAw4YYIABBhhggIHvDVQ9XZVzY+zOmgEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBqYYsOPE4hSL3sEiAwwwwAADDDDAAAMMdBswAzPWbUx9xhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggIG3Bg4x3kMn diff --git a/data/maps/template_start.tmx b/data/maps/template_start.tmx index 57f2dc6..fe182cf 100644 --- a/data/maps/template_start.tmx +++ b/data/maps/template_start.tmx @@ -2,6 +2,7 @@ + eAHt2zEKACAIAMB6TC+z/7+hHuCYYHBDi0OI3iJozDHCUwMGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggAEGGGCAAQYYYIABBhhggIFrYHPAAQMMMMAAAwww8L2BlfQwi7kxdmfNAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADXQzYZWexi0V5sMgAAwwwwAADDDDAAAPVBszAjFUb8z9jDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAAAMMMMAAAwwwwAADDDDAwFsDB/P6Qqk= diff --git a/data/saland/spells/base_spells.json b/data/saland/spells/base_spells.json index cc323ec..9ad49cc 100644 --- a/data/saland/spells/base_spells.json +++ b/data/saland/spells/base_spells.json @@ -21,24 +21,34 @@ } ,{ "name": "spell_create_block", - "tile": 607, + "tile": 606, + "tileset": "terrain", "type": "tile" } ,{ "icon": "", "name" : "spell_create_block", - "tile" : 28, + "tile" : 27, + "tileset": "terrain", "type" : "tile" } ,{ "icon" : "", "name" : "spell_create_block", - "tile" : 16, + "tile" : 15, + "tileset": "terrain", "type" : "tile" } ,{ "name" : "spell_create_ground", - "tile" : 1, + "tile" : 0, + "tileset": "terrain", + "type" : "tile" + } + ,{ + "name" : "spell_create_dungeon", + "tile" : 0, + "tileset": "tiles_dungeon", "type" : "tile" } ,{ diff --git a/src/sagotmx/tmx_struct.h b/src/sagotmx/tmx_struct.h index 9dc3a56..99fb545 100644 --- a/src/sagotmx/tmx_struct.h +++ b/src/sagotmx/tmx_struct.h @@ -695,6 +695,64 @@ inline void setTileOnLayerNumber(TileMap& m, int layer_number, int x, int y, uin *dp = tile; } +/** + * Returns the firstgid of the tileset whose source filename (without path and extension) + * matches short_name. For example, pass "terrain" to find the tileset loaded from "terrain.tsx". + * Returns 0 and prints a warning if not found. + */ +inline uint32_t getFirstGidForTilesetSource(const TileMap& tm, const std::string& short_name) { + for (const auto& ts : tm.tileset) { + if (!ts.source.empty()) { + std::string basename = ts.source; + size_t slash = basename.find_last_of("/\\"); + if (slash != std::string::npos) { + basename = basename.substr(slash + 1); + } + size_t dot = basename.rfind('.'); + if (dot != std::string::npos) { + basename = basename.substr(0, dot); + } + if (basename == short_name) { + return static_cast(ts.firstgid); + } + } + } + std::cerr << "Warning: tileset '" << short_name << "' not found in TileMap\n"; + return 0; +} + +/** + * Returns the number of tile columns in the tileset identified by short_name. + * Computed as image.width / tilewidth from the loaded tileset data. + * Returns 32 as fallback if not found or image data is unavailable. + */ +inline int getTilesetColumns(const TileMap& tm, const std::string& short_name) { + for (const auto& ts : tm.tileset) { + if (!ts.source.empty()) { + std::string basename = ts.source; + size_t slash = basename.find_last_of("/\\"); + if (slash != std::string::npos) { + basename = basename.substr(slash + 1); + } + size_t dot = basename.rfind('.'); + if (dot != std::string::npos) { + basename = basename.substr(0, dot); + } + if (basename == short_name) { + const TileSet* effective = &ts; + while (effective->alternativeSource) { + effective = effective->alternativeSource; + } + if (effective->tilewidth > 0 && effective->image.width > 0) { + return effective->image.width / effective->tilewidth; + } + return 32; + } + } + } + return 32; +} + } //tiled } //sago diff --git a/src/saland/Game.cpp b/src/saland/Game.cpp index 377a000..6373280 100644 --- a/src/saland/Game.cpp +++ b/src/saland/Game.cpp @@ -368,10 +368,10 @@ Game::Game() { data->spell_holder->init(); data->spell_holder->slot_spell.at(0) = data->spell_holder->get_spell_by_name("spell_fireball"); data->spell_holder->slot_spell.at(1) = data->spell_holder->get_spell_by_name("weapon_slash_long_knife"); - data->spell_holder->slot_spell.at(2) = data->spell_holder->get_spell_by_name("spell_create_block:607"); - data->spell_holder->slot_spell.at(3) = data->spell_holder->get_spell_by_name("spell_create_block:28"); - data->spell_holder->slot_spell.at(4) = data->spell_holder->get_spell_by_name("spell_create_block:16"); - data->spell_holder->slot_spell.at(5) = data->spell_holder->get_spell_by_name("spell_create_ground:1"); + data->spell_holder->slot_spell.at(2) = data->spell_holder->get_spell_by_name("spell_create_block:terrain:606"); + data->spell_holder->slot_spell.at(3) = data->spell_holder->get_spell_by_name("spell_create_block:terrain:27"); + data->spell_holder->slot_spell.at(4) = data->spell_holder->get_spell_by_name("spell_create_block:terrain:15"); + data->spell_holder->slot_spell.at(5) = data->spell_holder->get_spell_by_name("spell_create_ground:terrain:0"); data->spell_holder->slot_spell.at(6) = data->spell_holder->get_spell_by_name("spell_spawn_item:food_potato"); data->spell_holder->slot_spell.at(7) = data->spell_holder->get_spell_by_name("spell_spawn_item:barrel"); data->spell_holder->slot_spell.at(8) = data->spell_holder->get_spell_by_name("spell_spawn_item:tree_palm"); @@ -1118,7 +1118,7 @@ void Game::Update() { data->human->animation = "spellcast"; int base_tile_x = data->world_mouse_x/32; int base_tile_y = data->world_mouse_y/32; - int tile = data->spell_holder->slot_spell.at(data->spell_holder->slot_selected).tile; + uint32_t tile = resolveSpellTileGID(data->spell_holder->slot_spell.at(data->spell_holder->slot_selected), data->gameRegion.world.tm); int layer_number = data->gameRegion.world.ground2Layer; if (layer_number >= 0) { for (int dy = 0; dy < data->brushSize; ++dy) { @@ -1141,7 +1141,7 @@ void Game::Update() { data->human->animation = "spellcast"; int base_tile_x = data->world_mouse_x/32; int base_tile_y = data->world_mouse_y/32; - int tile = data->spell_holder->slot_spell.at(data->spell_holder->slot_selected).tile; + uint32_t tile = resolveSpellTileGID(data->spell_holder->slot_spell.at(data->spell_holder->slot_selected), data->gameRegion.world.tm); int layer_number = data->gameRegion.world.blockingLayer; bool anyTileChanged = false; for (int dy = 0; dy < data->brushSize; ++dy) { @@ -1153,6 +1153,7 @@ void Game::Update() { sago::tiled::setTileOnLayerNumber(data->gameRegion.world.tm, layer_number, tile_x, tile_y, tile); data->gameRegion.liqudHandler["water"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); data->gameRegion.liqudHandler["lava"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); + data->gameRegion.liqudHandler["dungeon"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); anyTileChanged = true; } } @@ -1180,6 +1181,35 @@ void Game::Update() { sago::tiled::setTileOnLayerNumber(data->gameRegion.world.tm, layer_number, tile_x, tile_y, tile); data->gameRegion.liqudHandler["water"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); data->gameRegion.liqudHandler["lava"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); + data->gameRegion.liqudHandler["dungeon"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); + anyTileChanged = true; + } + } + } + if (anyTileChanged) { + data->gameRegion.world.init_physics(data->gameRegion.physicsBox); + } + } + } + if (data->spell_holder->slot_spell.at(data->spell_holder->slot_selected).name == "spell_create_dungeon") { + if (data->human->castTimeRemaining == 0) { + data->human->castTimeRemaining = data->human->castTime; + data->human->animation = "spellcast"; + int base_tile_x = data->world_mouse_x/32; + int base_tile_y = data->world_mouse_y/32; + uint32_t tile = resolveSpellTileGID(data->spell_holder->slot_spell.at(data->spell_holder->slot_selected), data->gameRegion.world.tm); + int layer_number = data->gameRegion.world.blockingLayer; + bool anyTileChanged = false; + for (int dy = 0; dy < data->brushSize; ++dy) { + for (int dx = 0; dx < data->brushSize; ++dx) { + int tile_x = base_tile_x + dx; + int tile_y = base_tile_y + dy; + if (sago::tiled::tileInBound(data->gameRegion.world.tm, tile_x, tile_y) + && !(data->gameRegion.world.tile_protected(tile_x, tile_y)) ) { + sago::tiled::setTileOnLayerNumber(data->gameRegion.world.tm, layer_number, tile_x, tile_y, tile); + data->gameRegion.liqudHandler["dungeon"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); + data->gameRegion.liqudHandler["water"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); + data->gameRegion.liqudHandler["lava"].updateFirstTile(data->gameRegion.world.tm, tile_x, tile_y); anyTileChanged = true; } } diff --git a/src/saland/GameRegion.cpp b/src/saland/GameRegion.cpp index 7bf6624..9a2da40 100644 --- a/src/saland/GameRegion.cpp +++ b/src/saland/GameRegion.cpp @@ -279,7 +279,7 @@ void GameRegion::CreateLake(World& world, int tile_x, int tile_y) continue; } int layer_number = world.blockingLayer; - uint32_t tile = 28; + uint32_t tile = liqudHandler["water"].default_tile; sago::tiled::setTileOnLayerNumber(world.tm, layer_number, tile_x + i, tile_y + j, tile); liqudHandler["water"].updateFirstTile(world.tm, tile_x + i, tile_y + j); } @@ -321,22 +321,42 @@ void GameRegion::InitCommon() { placeables.clear(); physicsBox.reset(new b2World(b2Vec2(0.0f, 0.0f))); world.managed_bodies.clear(); +} + +void GameRegion::InitLiquidHandlers() { + liqudHandler.clear(); + + uint32_t terrain_firstgid = sago::tiled::getFirstGidForTilesetSource(world.tm, "terrain"); + if (terrain_firstgid == 0) { + terrain_firstgid = 1; // fallback for maps without a named terrain tileset + } + int terrain_cols = sago::tiled::getTilesetColumns(world.tm, "terrain"); liqudHandler["water"].blockingLayer = world.blockingLayer; liqudHandler["water"].blockingLayer_overlay_1 = world.blockingLayer_overlay_1; - liqudHandler["water"].setupTiles(28); + liqudHandler["water"].setupTiles(terrain_firstgid + 27, terrain_cols); + liqudHandler["lava"].blockingLayer = world.blockingLayer; liqudHandler["lava"].blockingLayer_overlay_1 = world.blockingLayer_overlay_1; - liqudHandler["lava"].setupTiles(16); + liqudHandler["lava"].setupTiles(terrain_firstgid + 15, terrain_cols); + liqudHandler["ground"].blockingLayer = world.ground2Layer; liqudHandler["ground"].blockingLayer_overlay_1 = world.ground2OverlayLayer; - liqudHandler["ground"].setupTiles(1); - + liqudHandler["ground"].setupTiles(terrain_firstgid + 0, terrain_cols); + + uint32_t dungeon_firstgid = sago::tiled::getFirstGidForTilesetSource(world.tm, "tiles_dungeon"); + if (dungeon_firstgid > 0) { + int dungeon_cols = sago::tiled::getTilesetColumns(world.tm, "tiles_dungeon"); + liqudHandler["dungeon"].blockingLayer = world.blockingLayer; + liqudHandler["dungeon"].blockingLayer_overlay_1 = world.blockingLayer_overlay_1; + liqudHandler["dungeon"].setupTiles(dungeon_firstgid, dungeon_cols); + } } void GameRegion::InitDungeon(const std::string& dungeonName, const std::string& dungeonType, bool forceResetWorld) { InitCommon(); world.init(physicsBox, "maps/"+dungeonName+".tmx"); + InitLiquidHandlers(); } @@ -354,6 +374,7 @@ void GameRegion::Init(int x, int y, const std::string& worldName, bool forceRese InitCommon(); ScanPrefabs("prefabs01"); world.init(physicsBox, loadMap); + InitLiquidHandlers(); const std::vector& object_groups = world.tm.object_groups; diff --git a/src/saland/GameRegion.hpp b/src/saland/GameRegion.hpp index 2d3a534..bcf2f66 100644 --- a/src/saland/GameRegion.hpp +++ b/src/saland/GameRegion.hpp @@ -75,6 +75,7 @@ private: int region_y = 0; std::string mapFileName = "maps/sample1.tmx"; void InitCommon(); + void InitLiquidHandlers(); }; #endif /* GAMEREGION_HPP */ diff --git a/src/saland/GameSpellState.cpp b/src/saland/GameSpellState.cpp index 654a7c8..283d785 100644 --- a/src/saland/GameSpellState.cpp +++ b/src/saland/GameSpellState.cpp @@ -72,8 +72,12 @@ void GameSpellState::GameSpellState::Draw(SDL_Renderer* target) { if (current_spell.icon.length() > 0) { globalData.spriteHolder.get()->GetSprite(current_spell.icon).Draw(target, SDL_GetTicks(), 36+i*56, 36, resize); } - if (current_spell.tile > 0) { - DrawTile(target, globalData.spriteHolder.get(), *tm, current_spell.tile, 20+i*BOX_SPACING, 20, resize); + bool hasTile = (current_spell.tile > 0) || (!current_spell.tileset_name.empty()); + if (hasTile && tm != nullptr) { + uint32_t gid = resolveSpellTileGID(current_spell, *tm); + if (gid > 0) { + DrawTile(target, globalData.spriteHolder.get(), *tm, gid, 20+i*BOX_SPACING, 20, resize); + } } } if (!data->spellSelectActive) { @@ -87,8 +91,12 @@ void GameSpellState::GameSpellState::Draw(SDL_Renderer* target) { if (current_spell.icon.length() > 0) { globalData.spriteHolder.get()->GetSprite(current_spell.icon).Draw(target, SDL_GetTicks(), x+BOX_SIZE/2, y+BOX_SIZE/2, resize); } - if (current_spell.tile > 0) { - DrawTile(target, globalData.spriteHolder.get(), *tm, current_spell.tile, x+10, y+10, resize); + bool hasTile = (current_spell.tile > 0) || (!current_spell.tileset_name.empty()); + if (hasTile && tm != nullptr) { + uint32_t gid = resolveSpellTileGID(current_spell, *tm); + if (gid > 0) { + DrawTile(target, globalData.spriteHolder.get(), *tm, gid, x+10, y+10, resize); + } } } } diff --git a/src/saland/model/spells.cpp b/src/saland/model/spells.cpp index 256f452..cc993e9 100644 --- a/src/saland/model/spells.cpp +++ b/src/saland/model/spells.cpp @@ -33,7 +33,9 @@ void SpellHolder::add_spell(const Spell& spell) { if (spell.item_name[0]) { name = name + ":" + spell.item_name; } - if (spell.tile) { + if (!spell.tileset_name.empty()) { + name = name + ":" + spell.tileset_name + ":" + std::to_string(spell.tile); + } else if (spell.tile) { name = name + ":" + std::to_string(spell.tile); } spellIndex[name] = spells.size() - 1; @@ -68,6 +70,9 @@ void SpellHolder::ReadSpellFile(const std::string& filename) { if (member.name == "item_name") { spell.item_name = member.value.GetString(); } + if (member.name == "tileset") { + spell.tileset_name = member.value.GetString(); + } if (member.name == "tile") { spell.tile = member.value.GetInt64(); } @@ -132,3 +137,14 @@ const Spell& SpellHolder::get_spell_by_name(const std::string& spell_name) const const Spell& SpellHolder::get_spell_clear_tile() const { return clearTileSpell; } + +uint32_t resolveSpellTileGID(const Spell& spell, const sago::tiled::TileMap& tm) { + if (spell.tileset_name.empty()) { + return static_cast(spell.tile); + } + uint32_t firstgid = sago::tiled::getFirstGidForTilesetSource(tm, spell.tileset_name); + if (firstgid == 0) { + return static_cast(spell.tile); + } + return firstgid + static_cast(spell.tile); +} diff --git a/src/saland/model/spells.hpp b/src/saland/model/spells.hpp index c112d2b..f8ec794 100644 --- a/src/saland/model/spells.hpp +++ b/src/saland/model/spells.hpp @@ -28,6 +28,7 @@ https://github.com/sago007/saland #include #include #include +#include "../../sagotmx/tmx_struct.h" enum class SpellCursorType { dot = 0, tile=1, shpere = 2 }; @@ -35,7 +36,8 @@ struct Spell { std::string name; std::string icon; std::string item_name; - int tile = 0; //Number reference to a tile, we might also need a tileset at some point + int tile = 0; //Local (0-indexed) tile offset within the named tileset, or raw GID when tileset_name is empty + std::string tileset_name; //Short name of the tileset source (e.g. "terrain", "tiles_dungeon"). Empty = raw GID in tile field. SpellCursorType type = SpellCursorType::dot; }; @@ -58,4 +60,10 @@ public: void ReadSpellFile(const std::string& filename); }; +/** + * Resolves the GID for a spell's tile. If tileset_name is set, returns firstgid + tile (local offset). + * If tileset_name is empty, returns tile directly as a raw GID (backward compatibility). + */ +uint32_t resolveSpellTileGID(const Spell& spell, const sago::tiled::TileMap& tm); + #endif //MODEL_SPELLS_HPP diff --git a/src/terrain/WaterHandler.cpp b/src/terrain/WaterHandler.cpp index b395443..1196ecd 100644 --- a/src/terrain/WaterHandler.cpp +++ b/src/terrain/WaterHandler.cpp @@ -29,9 +29,8 @@ WaterHandler::WaterHandler() { setupTiles(default_tile); } -void WaterHandler::setupTiles(uint32_t start_tile) { +void WaterHandler::setupTiles(uint32_t start_tile, int tile_count_width) { default_tile = start_tile; - int tile_count_width = 32; tile_map["11111110"] = start_tile+1; tile_map["11111011"] = start_tile+2; tile_map["11011111"] = start_tile+tile_count_width+1; @@ -53,10 +52,12 @@ void WaterHandler::setupTiles(uint32_t start_tile) { tile_map["11111111b"] = start_tile+tile_count_width*5+1; tile_map["11111111c"] = start_tile+tile_count_width*5+2; - tiles = {start_tile, start_tile+1, start_tile+2, start_tile+33, start_tile+34, - start_tile+35, start_tile+64, start_tile+65, start_tile+66, start_tile+96, - start_tile+97, start_tile+98, start_tile+128, start_tile+129, start_tile+130, - start_tile+160, start_tile+161, start_tile+162 + tiles = {start_tile, start_tile+1, start_tile+2, + start_tile+tile_count_width+1, start_tile+tile_count_width+2, start_tile+tile_count_width+3, + start_tile+tile_count_width*2, start_tile+tile_count_width*2+1, start_tile+tile_count_width*2+2, + start_tile+tile_count_width*3, start_tile+tile_count_width*3+1, start_tile+tile_count_width*3+2, + start_tile+tile_count_width*4, start_tile+tile_count_width*4+1, start_tile+tile_count_width*4+2, + start_tile+tile_count_width*5, start_tile+tile_count_width*5+1, start_tile+tile_count_width*5+2 }; } diff --git a/src/terrain/WaterHandler.hpp b/src/terrain/WaterHandler.hpp index 3780df6..aa798f9 100644 --- a/src/terrain/WaterHandler.hpp +++ b/src/terrain/WaterHandler.hpp @@ -36,7 +36,7 @@ struct WaterHandler { WaterHandler(); - void setupTiles(uint32_t start_tile); + void setupTiles(uint32_t start_tile, int tile_count_width = 32); uint32_t getTile(sago::tiled::TileMap& tm, int x, int y, uint32_t& overlay_tile);