Cache animated tile speed, use btree map for animated tiles

pull/206/head
Jonathan G Rennison 4 years ago
parent 72a7ae25e8
commit 2ad446369d

@ -38,6 +38,7 @@ add_files(
airport.h
airport_gui.cpp
animated_tile.cpp
animated_tile.h
animated_tile_func.h
articulated_vehicles.cpp
articulated_vehicles.h

@ -8,16 +8,19 @@
/** @file animated_tile.cpp Everything related to animated tiles. */
#include "stdafx.h"
#include "animated_tile.h"
#include "core/alloc_func.hpp"
#include "core/smallvec_type.hpp"
#include "tile_cmd.h"
#include "viewport_func.h"
#include "framerate_type.h"
#include "date_func.h"
#include "3rdparty/cpp-btree/btree_map.h"
#include "safeguards.h"
/** The table/list with animated tiles. */
std::vector<TileIndex> _animated_tiles;
btree::btree_map<TileIndex, AnimatedTileInfo> _animated_tiles;
/**
* Removes the given tile from the animated tile table.
@ -25,14 +28,43 @@ std::vector<TileIndex> _animated_tiles;
*/
void DeleteAnimatedTile(TileIndex tile)
{
auto to_remove = std::find(_animated_tiles.begin(), _animated_tiles.end(), 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);
auto to_remove = _animated_tiles.find(tile);
if (to_remove != _animated_tiles.end() && !to_remove->second.pending_deletion) {
to_remove->second.pending_deletion = true;
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
}
}
static void UpdateAnimatedTileSpeed(TileIndex tile, AnimatedTileInfo &info)
{
extern uint8 GetAnimatedTileSpeed_Town(TileIndex tile);
extern uint8 GetAnimatedTileSpeed_Station(TileIndex tile);
extern uint8 GetAnimatedTileSpeed_Industry(TileIndex tile);
extern uint8 GetNewObjectTileAnimationSpeed(TileIndex tile);
switch (GetTileType(tile)) {
case MP_HOUSE:
info.speed = GetAnimatedTileSpeed_Town(tile);
break;
case MP_STATION:
info.speed = GetAnimatedTileSpeed_Station(tile);
break;
case MP_INDUSTRY:
info.speed = GetAnimatedTileSpeed_Industry(tile);
break;
case MP_OBJECT:
info.speed = GetNewObjectTileAnimationSpeed(tile);
break;
default:
info.speed = 0;
break;
}
}
/**
* Add the given tile to the animated tile table (if it does not exist
* on that table yet). Also increases the size of the table if necessary.
@ -41,7 +73,18 @@ void DeleteAnimatedTile(TileIndex tile)
void AddAnimatedTile(TileIndex tile)
{
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
include(_animated_tiles, tile);
AnimatedTileInfo &info = _animated_tiles[tile];
UpdateAnimatedTileSpeed(tile, info);
info.pending_deletion = false;
}
int GetAnimatedTileSpeed(TileIndex tile)
{
const auto iter = _animated_tiles.find(tile);
if (iter != _animated_tiles.end() && !iter->second.pending_deletion) {
return iter->second.speed;
}
return -1;
}
/**
@ -56,40 +99,53 @@ void AnimateAnimatedTiles()
PerformanceAccumulator framerate(PFE_GL_LANDSCAPE);
const TileIndex *ti = _animated_tiles.data();
while (ti < _animated_tiles.data() + _animated_tiles.size()) {
const TileIndex curr = *ti;
switch (GetTileType(curr)) {
case MP_HOUSE:
AnimateTile_Town(curr);
break;
const uint32 ticks = (uint) _scaled_tick_counter;
const uint8 max_speed = (ticks == 0) ? 32 : FindFirstBit(ticks);
auto iter = _animated_tiles.begin();
while (iter != _animated_tiles.end()) {
if (iter->second.pending_deletion) {
iter = _animated_tiles.erase(iter);
continue;
}
if (iter->second.speed <= max_speed) {
const TileIndex curr = iter->first;
switch (GetTileType(curr)) {
case MP_HOUSE:
AnimateTile_Town(curr);
break;
case MP_STATION:
AnimateTile_Station(curr);
break;
case MP_STATION:
AnimateTile_Station(curr);
break;
case MP_INDUSTRY:
AnimateTile_Industry(curr);
break;
case MP_INDUSTRY:
AnimateTile_Industry(curr);
break;
case MP_OBJECT:
AnimateTile_Object(curr);
break;
case MP_OBJECT:
AnimateTile_Object(curr);
break;
default:
NOT_REACHED();
default:
NOT_REACHED();
}
}
++iter;
}
}
/* 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;
void UpdateAllAnimatedTileSpeeds()
{
auto iter = _animated_tiles.begin();
while (iter != _animated_tiles.end()) {
if (iter->second.pending_deletion) {
iter = _animated_tiles.erase(iter);
continue;
}
UpdateAnimatedTileSpeed(iter->first, iter->second);
++iter;
}
}

@ -0,0 +1,23 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file animated_tile.h %Tile animation! */
#ifndef ANIMATED_TILE_H
#define ANIMATED_TILE_H
#include "tile_type.h"
#include "3rdparty/cpp-btree/btree_map.h"
struct AnimatedTileInfo {
uint8 speed = 0;
bool pending_deletion = false;
};
extern btree::btree_map<TileIndex, AnimatedTileInfo> _animated_tiles;
#endif /* ANIMATED_TILE_H */

@ -15,6 +15,7 @@
void AddAnimatedTile(TileIndex tile);
void DeleteAnimatedTile(TileIndex tile);
void AnimateAnimatedTiles();
void UpdateAllAnimatedTileSpeeds();
void InitializeAnimatedTiles();
#endif /* ANIMATED_TILE_FUNC_H */

@ -724,6 +724,47 @@ void AnimateTile_Industry(TileIndex tile)
}
}
uint8 GetAnimatedTileSpeed_Industry(TileIndex tile)
{
IndustryGfx gfx = GetIndustryGfx(tile);
if (GetIndustryTileSpec(gfx)->animation.status != ANIM_STATUS_NO_ANIMATION) {
return GetNewIndustryTileAnimationSpeed(tile);
}
switch (gfx) {
case GFX_SUGAR_MINE_SIEVE:
return 1;
case GFX_TOFFEE_QUARY:
return 2;
case GFX_BUBBLE_CATCHER:
return 1;
case GFX_POWERPLANT_SPARKS:
return 2;
case GFX_TOY_FACTORY:
return 1;
case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
return 2;
case GFX_OILWELL_ANIMATED_1:
case GFX_OILWELL_ANIMATED_2:
case GFX_OILWELL_ANIMATED_3:
return 3;
default:
return 0;
}
}
static void CreateChimneySmoke(TileIndex tile)
{
uint x = TileX(tile) * TILE_SIZE;

@ -312,3 +312,10 @@ void AirportAnimationTrigger(Station *st, AirpAnimationTrigger trigger, CargoID
}
}
uint8 GetAirportTileAnimationSpeed(TileIndex tile)
{
const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
if (ats == nullptr) return 0;
return AirportTileAnimationBase::GetAnimationSpeed(ats);
}

@ -86,6 +86,7 @@ StationGfx GetTranslatedAirportTileID(StationGfx gfx);
void AnimateAirportTile(TileIndex tile);
void AirportTileAnimationTrigger(Station *st, TileIndex tile, AirpAnimationTrigger trigger, CargoID cargo_type = CT_INVALID);
void AirportAnimationTrigger(Station *st, AirpAnimationTrigger trigger, CargoID cargo_type = CT_INVALID);
uint8 GetAirportTileAnimationSpeed(TileIndex tile);
bool DrawNewAirportTile(TileInfo *ti, Station *st, StationGfx gfx, const AirportTileSpec *airts);
#endif /* NEWGRF_AIRPORTTILES_H */

@ -133,4 +133,10 @@ struct AnimationBase {
* result are not empty, it is a sound effect. */
if (GB(callback, 8, 7) != 0 && _settings_client.sound.ambient) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
}
static uint8 GetAnimationSpeed(const Tspec *spec)
{
if (HasBit(spec->callback_mask, Tbase::cbm_animation_speed)) return 0;
return spec->animation.speed;
}
};

@ -662,6 +662,14 @@ void AnimateNewHouseConstruction(TileIndex tile)
}
}
uint8 GetNewHouseTileAnimationSpeed(TileIndex tile)
{
const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
if (hs == nullptr) return 0;
return HouseAnimationBase::GetAnimationSpeed(hs);
}
/**
* Check if GRF allows a given house to be constructed (callback 17)
* @param house_id house type

@ -144,6 +144,7 @@ void DrawNewHouseTile(TileInfo *ti, HouseID house_id);
void DrawNewHouseTileInGUI(int x, int y, HouseID house_id, bool ground);
void AnimateNewHouseTile(TileIndex tile);
void AnimateNewHouseConstruction(TileIndex tile);
uint8 GetNewHouseTileAnimationSpeed(TileIndex tile);
uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town = nullptr, TileIndex tile = INVALID_TILE,
bool not_yet_constructed = false, uint8 initial_random_bits = 0, CargoTypes watched_cargo_triggers = 0);

@ -300,6 +300,14 @@ bool StartStopIndustryTileAnimation(const Industry *ind, IndustryAnimationTrigge
return ret;
}
uint8 GetNewIndustryTileAnimationSpeed(TileIndex tile)
{
const IndustryTileSpec *itspec = GetIndustryTileSpec(GetIndustryGfx(tile));
if (itspec == nullptr) return 0;
return IndustryAnimationBase::GetAnimationSpeed(itspec);
}
/**
* Trigger random triggers for an industry tile and reseed its random bits.
* @param tile Industry tile to trigger.

@ -64,6 +64,7 @@ CommandCost PerformIndustryTileSlopeCheck(TileIndex ind_base_tile, TileIndex ind
void AnimateNewIndustryTile(TileIndex tile);
bool StartStopIndustryTileAnimation(TileIndex tile, IndustryAnimationTrigger iat, uint32 random = Random());
bool StartStopIndustryTileAnimation(const Industry *ind, IndustryAnimationTrigger iat);
uint8 GetNewIndustryTileAnimationSpeed(TileIndex tile);
/** Available industry tile triggers. */

@ -530,6 +530,14 @@ void AnimateNewObjectTile(TileIndex tile)
ObjectAnimationBase::AnimateTile(spec, Object::GetByTile(tile), tile, (spec->flags & OBJECT_FLAG_ANIM_RANDOM_BITS) != 0);
}
uint8 GetNewObjectTileAnimationSpeed(TileIndex tile)
{
const ObjectSpec *spec = ObjectSpec::GetByTile(tile);
if (spec == nullptr || !(spec->flags & OBJECT_FLAG_ANIMATION)) return 0;
return ObjectAnimationBase::GetAnimationSpeed(spec);
}
/**
* Trigger the update of animation on a single tile.
* @param o The object that got triggered.

@ -163,6 +163,7 @@ uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, cons
void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec);
void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec, uint8 view);
void AnimateNewObjectTile(TileIndex tile);
uint8 GetNewObjectTileAnimationSpeed(TileIndex tile);
void TriggerObjectTileAnimation(Object *o, TileIndex tile, ObjectAnimationTrigger trigger, const ObjectSpec *spec);
void TriggerObjectAnimation(Object *o, ObjectAnimationTrigger trigger, const ObjectSpec *spec);

@ -924,6 +924,14 @@ void AnimateStationTile(TileIndex tile)
StationAnimationBase::AnimateTile(ss, BaseStation::GetByTile(tile), tile, HasBit(ss->flags, SSF_CB141_RANDOM_BITS));
}
uint8 GetStationTileAnimationSpeed(TileIndex tile)
{
const StationSpec *ss = GetStationSpec(tile);
if (ss == nullptr) return 0;
return StationAnimationBase::GetAnimationSpeed(ss);
}
void TriggerStationAnimation(BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type)
{
/* List of coverage areas for each animation trigger */

@ -204,6 +204,7 @@ void DeallocateSpecFromStation(BaseStation *st, byte specindex);
bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID sclass, uint station);
void AnimateStationTile(TileIndex tile);
uint8 GetStationTileAnimationSpeed(TileIndex tile);
void TriggerStationAnimation(BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type = CT_INVALID);
void TriggerStationRandomisation(Station *st, TileIndex tile, StationRandomTrigger trigger, CargoID cargo_type = CT_INVALID);
void StationUpdateCachedTriggers(BaseStation *st);

@ -63,6 +63,7 @@
#include "../bridge_signal_map.h"
#include "../water.h"
#include "../settings_func.h"
#include "../animated_tile.h"
#include "saveload_internal.h"
@ -2461,16 +2462,9 @@ bool AfterLoadGame()
/* Animated tiles would sometimes not be actually animated or
* in case of old savegames duplicate. */
extern std::vector<TileIndex> _animated_tiles;
for (auto tile = _animated_tiles.begin(); tile < _animated_tiles.end(); /* Nothing */) {
for (auto tile = _animated_tiles.begin(); tile != _animated_tiles.end(); /* Nothing */) {
/* Remove if tile is not animated */
bool remove = _tile_type_procs[GetTileType(*tile)]->animate_tile_proc == nullptr;
/* and remove if duplicate */
for (auto j = _animated_tiles.begin(); !remove && j < tile; j++) {
remove = *tile == *j;
}
bool remove = _tile_type_procs[GetTileType(tile->first)]->animate_tile_proc == nullptr;
if (remove) {
tile = _animated_tiles.erase(tile);
@ -3806,6 +3800,10 @@ bool AfterLoadGame()
RecalculateRoadCachedOneWayStates();
}
if (SlXvIsFeatureMissing(XSLFI_ANIMATED_TILE_EXTRA)) {
UpdateAllAnimatedTileSpeeds();
}
InitializeRoadGUI();
/* This needs to be done after conversion. */
@ -3928,4 +3926,5 @@ void ReloadNewGRFData()
CheckTrainsLengths();
AfterLoadTemplateVehiclesUpdateImage();
AfterLoadTemplateVehiclesUpdateProperties();
UpdateAllAnimatedTileSpeeds();
}

@ -8,6 +8,7 @@
/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */
#include "../stdafx.h"
#include "../animated_tile.h"
#include "../tile_type.h"
#include "../core/alloc_func.hpp"
#include "../core/smallvec_type.hpp"
@ -16,15 +17,21 @@
#include "../safeguards.h"
extern std::vector<TileIndex> _animated_tiles;
/**
* Save the ANIT chunk.
*/
static void Save_ANIT()
{
SlSetLength(_animated_tiles.size() * sizeof(_animated_tiles.front()));
SlArray(_animated_tiles.data(), _animated_tiles.size(), SLE_UINT32);
uint count = 0;
for (const auto &it : _animated_tiles) {
if (!it.second.pending_deletion) count++;
}
SlSetLength(count * 5);
for (const auto &it : _animated_tiles) {
if (it.second.pending_deletion) continue;
SlWriteUint32(it.first);
SlWriteByte(it.second.speed);
}
}
/**
@ -40,15 +47,26 @@ static void Load_ANIT()
for (int i = 0; i < 256; i++) {
if (anim_list[i] == 0) break;
_animated_tiles.push_back(anim_list[i]);
_animated_tiles[anim_list[i]] = {};
}
return;
}
uint count = (uint)SlGetFieldLength() / sizeof(_animated_tiles.front());
_animated_tiles.clear();
_animated_tiles.resize(_animated_tiles.size() + count);
SlArray(_animated_tiles.data(), count, SLE_UINT32);
if (SlXvIsFeaturePresent(XSLFI_ANIMATED_TILE_EXTRA)) {
uint count = (uint)SlGetFieldLength() / 5;
for (uint i = 0; i < count; i++) {
TileIndex tile = SlReadUint32();
AnimatedTileInfo info = {};
info.speed = SlReadByte();
_animated_tiles[tile] = info;
}
} else {
uint count = (uint)SlGetFieldLength() / 4;
for (uint i = 0; i < count; i++) {
_animated_tiles[SlReadUint32()] = {};
}
}
}
/**

@ -139,6 +139,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ONE_WAY_DT_ROAD_STOP, XSCF_NULL, 1, 1, "one_way_dt_road_stop", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_ROAD_STATE, XSCF_NULL, 1, 1, "one_way_road_state", nullptr, nullptr, nullptr },
{ XSLFI_VENC_CHUNK, XSCF_IGNORABLE_ALL, 1, 1, "venc_chunk", nullptr, nullptr, "VENC" },
{ XSLFI_ANIMATED_TILE_EXTRA, XSCF_NULL, 1, 1, "animated_tile_extra", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};

@ -93,6 +93,7 @@ enum SlXvFeatureIndex {
XSLFI_ONE_WAY_DT_ROAD_STOP, ///< One-way drive-through road stops
XSLFI_ONE_WAY_ROAD_STATE, ///< One-way road state cache
XSLFI_VENC_CHUNK, ///< VENC chunk
XSLFI_ANIMATED_TILE_EXTRA, ///< Animated tile extra info
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -26,6 +26,7 @@
#include "../engine_func.h"
#include "../company_base.h"
#include "../disaster_vehicle.h"
#include "../animated_tile.h"
#include "../core/smallvec_type.hpp"
#include "saveload_internal.h"
#include "oldloader.h"
@ -483,7 +484,6 @@ static inline uint RemapOrderIndex(uint x)
return _savegame_type == SGT_TTO ? (x - 0x1AC4) / 2 : (x - 0x1C18) / 2;
}
extern std::vector<TileIndex> _animated_tiles;
extern char *_old_name_array;
static uint32 _old_town_index;
@ -643,7 +643,7 @@ static bool LoadOldAnimTileList(LoadgameState *ls, int num)
/* The first zero in the loaded array indicates the end of the list. */
for (int i = 0; i < 256; i++) {
if (anim_list[i] == 0) break;
_animated_tiles.push_back(anim_list[i]);
_animated_tiles[anim_list[i]] = {};
}
return true;

@ -3553,6 +3553,18 @@ void AnimateTile_Station(TileIndex tile)
}
}
uint8 GetAnimatedTileSpeed_Station(TileIndex tile)
{
if (HasStationRail(tile)) {
return GetStationTileAnimationSpeed(tile);
}
if (IsAirport(tile)) {
AnimateAirportTile(tile);
}
return 0;
}
static bool ClickTile_Station(TileIndex tile)
{

@ -432,6 +432,14 @@ static Foundation GetFoundation_Town(TileIndex tile, Slope tileh)
return FlatteningFoundation(tileh);
}
uint8 GetAnimatedTileSpeed_Town(TileIndex tile)
{
if (GetHouseType(tile) >= NEW_HOUSE_OFFSET) {
return GetNewHouseTileAnimationSpeed(tile);
}
return 2;
}
/**
* Animate a tile for a town
* Only certain houses can be animated

Loading…
Cancel
Save