diff --git a/docs/landscape.html b/docs/landscape.html index d96695241c..94b7795a68 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -740,6 +740,7 @@ +
  • m6: bits 1..0: animated tile state
  • m7 : @@ -1651,6 +1654,7 @@
  • m2: index into the array of objects, bits 0 to 15 (upper bits in m5)
  • m3: random bits
  • m5: index into the array of objects, bits 16 to 23 (lower bits in m2)
  • +
  • m6 bits 1..0: animated tile state
  • m7: animation counter
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 1fbf4b4bb3..23bd4980b8 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -159,7 +159,7 @@ the array so you can quickly see what is used and what is not. 1 XOXXXXX XXXX XXXX XXXX XXXX - XXXX XXOO + XXXX XXXX XXXX XXXX OOOO OOOO OOOO OOOO @@ -188,7 +188,7 @@ the array so you can quickly see what is used and what is not. XXXX OXXX XXXX XXXX XXXX XXXX - OXXX XXOO + OXXX XXXX XXXX XXXX OOOO OOOO OOXX XXXX @@ -201,7 +201,7 @@ the array so you can quickly see what is used and what is not. XXXX OOOO OOXX XXXX OOOO OXXX - OXXX XOOO + OXXX XOXX OOOX XXXX OOOO XXXX XX XXXXXX @@ -280,7 +280,7 @@ the array so you can quickly see what is used and what is not. XXXX XXXX XXXX XXXX XXXX XXXX - OOXXX XOO + OOXXX XXX XXXX XXXX OOOO OOOO OOOO OOOO @@ -313,7 +313,7 @@ the array so you can quickly see what is used and what is not. XXXX XXXX OOOO OOOO XXXX XXXX - OOOO OOOO + OOOO OOXX XXXX XXXX OOOO OOOO OOOO OOOO diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5f7847ff8a..8f7cfdcb2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ add_files( airport_gui.cpp animated_tile.cpp animated_tile_func.h + animated_tile_map.h articulated_vehicles.cpp articulated_vehicles.h autocompletion.cpp diff --git a/src/animated_tile.cpp b/src/animated_tile.cpp index 86884d6ff9..694ba5f52d 100644 --- a/src/animated_tile.cpp +++ b/src/animated_tile.cpp @@ -8,7 +8,7 @@ /** @file animated_tile.cpp Everything related to animated tiles. */ #include "stdafx.h" -#include "core/container_func.hpp" +#include "animated_tile_map.h" #include "tile_cmd.h" #include "viewport_func.h" #include "framerate_type.h" @@ -19,16 +19,13 @@ std::vector _animated_tiles; /** - * Removes the given tile from the animated tile table. + * Stops animation on the given tile. * @param tile the tile to remove */ void DeleteAnimatedTile(TileIndex tile) { - auto to_remove = std::ranges::find(_animated_tiles, tile); - if (to_remove != _animated_tiles.end()) { - /* The order of the remaining elements must stay the same, otherwise the animation loop may miss a tile. */ - _animated_tiles.erase(to_remove); - } + /* If the tile was animated, mark it for deletion from the tile list on the next animation loop. */ + if (GetAnimatedTileState(tile) == AnimatedTileState::Animated) SetAnimatedTileState(tile, AnimatedTileState::Deleted); } /** @@ -39,7 +36,17 @@ void DeleteAnimatedTile(TileIndex tile) void AddAnimatedTile(TileIndex tile, bool mark_dirty) { if (mark_dirty) MarkTileDirtyByTile(tile); - include(_animated_tiles, tile); + + const AnimatedTileState state = GetAnimatedTileState(tile); + + /* Tile is already animated so nothing needs to happen. */ + if (state == AnimatedTileState::Animated) return; + + /* Tile has no previous animation state, so add to the tile list. If the state is anything + * other than None then the tile will still be in the list and does not need to be added again. */ + if (state == AnimatedTileState::None) _animated_tiles.push_back(tile); + + SetAnimatedTileState(tile, AnimatedTileState::Animated); } /** @@ -47,22 +54,29 @@ void AddAnimatedTile(TileIndex tile, bool mark_dirty) */ void AnimateAnimatedTiles() { - PerformanceAccumulator framerate(PFE_GL_LANDSCAPE); + PerformanceAccumulator landscape_framerate(PFE_GL_LANDSCAPE); - const TileIndex *ti = _animated_tiles.data(); - while (ti < _animated_tiles.data() + _animated_tiles.size()) { - const TileIndex curr = *ti; - AnimateTile(curr); - /* During the AnimateTile call, DeleteAnimatedTile could have been called, - * deleting an element we've already processed and pushing the rest one - * slot to the left. We can detect this by checking whether the index - * in the current slot has changed - if it has, an element has been deleted, - * and we should process the current slot again instead of going forward. - * NOTE: this will still break if more than one animated tile is being - * deleted during the same AnimateTile call, but no code seems to - * be doing this anyway. - */ - if (*ti == curr) ++ti; + for (auto it = std::begin(_animated_tiles); it != std::end(_animated_tiles); /* nothing */) { + TileIndex &tile = *it; + + if (GetAnimatedTileState(tile) != AnimatedTileState::Animated) { + /* Tile should not be animated any more, mark it as not animated and erase it from the list. */ + SetAnimatedTileState(tile, AnimatedTileState::None); + + /* Removing the last entry, no need to swap and continue. */ + if (std::next(it) == std::end(_animated_tiles)) { + _animated_tiles.pop_back(); + break; + } + + /* Replace the current list entry with the back of the list to avoid moving elements. */ + *it = _animated_tiles.back(); + _animated_tiles.pop_back(); + continue; + } + + AnimateTile(tile); + ++it; } } diff --git a/src/animated_tile_map.h b/src/animated_tile_map.h new file mode 100644 index 0000000000..4103a948e4 --- /dev/null +++ b/src/animated_tile_map.h @@ -0,0 +1,44 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file animated_tile_map.h Maps accessors for animated tiles. */ + +#ifndef ANIMATED_TILE_MAP_H +#define ANIMATED_TILE_MAP_H + +#include "core/bitmath_func.hpp" +#include "map_func.h" + +/** + * Animation state of a possibly-animated tile. + */ +enum class AnimatedTileState : uint8_t { + None = 0, ///< Tile is not animated. + Deleted = 1, ///< Tile was animated but should be removed. + Animated = 3, ///< Tile is animated. +}; + +/** + * Get the animated state of a tile. + * @param t The tile. + * @returns true iff the tile is animated. + */ +inline AnimatedTileState GetAnimatedTileState(Tile t) +{ + return static_cast(GB(t.m6(), 0, 2)); +} + +/** + * Set the animated state of a tile. + * @param t The tile. + */ +inline void SetAnimatedTileState(Tile t, AnimatedTileState state) +{ + SB(t.m6(), 0, 2, to_underlying(state)); +} + +#endif /* ANIMATED_TILE_MAP_H */ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 7c04967754..c39ccf6c46 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -43,6 +43,7 @@ #include "../game/game.hpp" #include "../town.h" #include "../economy_base.h" +#include "../animated_tile_map.h" #include "../animated_tile_func.h" #include "../subsidy_base.h" #include "../subsidy_func.h" @@ -2225,6 +2226,23 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_ANIMATED_TILE_STATE_IN_MAP)) { + /* Animated tile state is stored in the map array, allowing + * quicker addition and deletion of animated tiles. */ + + extern std::vector _animated_tiles; + + for (auto t : Map::Iterate()) { + /* Ensure there is no spurious animated tile state. */ + if (MayAnimateTile(t)) SetAnimatedTileState(t, AnimatedTileState::None); + } + + /* Set animated flag for all valid animated tiles. */ + for (const TileIndex &tile : _animated_tiles) { + if (tile != INVALID_TILE) SetAnimatedTileState(tile, AnimatedTileState::Animated); + } + } + if (IsSavegameVersionBefore(SLV_124) && !IsSavegameVersionBefore(SLV_1)) { /* The train station tile area was added, but for really old (TTDPatch) it's already valid. */ for (Waypoint *wp : Waypoint::Iterate()) { diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 7bb1c6d8d0..be0c3d9683 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -393,6 +393,7 @@ enum SaveLoadVersion : uint16_t { SLV_NONFLOODING_WATER_TILES, ///< 345 PR#13013 Store water tile non-flooding state. SLV_PATH_CACHE_FORMAT, ///< 346 PR#12345 Vehicle path cache format changed. + SLV_ANIMATED_TILE_STATE_IN_MAP, ///< 347 PR#13082 Animated tile state saved for improved performance. SL_MAX_VERSION, ///< Highest possible saveload version };