Codechange: create MutableSpriteCache to remove the need to cast Vehicle to a mutable type in render methods

This commit is contained in:
Matt Kimber 2021-01-03 13:32:58 +00:00 committed by Patric Stout
parent eeb88e87d8
commit 9b28b15e67
11 changed files with 74 additions and 77 deletions

View File

@ -341,8 +341,8 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
v->date_of_last_service = _date;
v->build_year = u->build_year = _cur_year;
v->sprite_seq.Set(SPR_IMG_QUERY);
u->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
u->random_bits = VehicleRandomBits();
@ -374,7 +374,7 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *
w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
w->spritenum = 0xFF;
w->subtype = AIR_ROTOR;
w->sprite_seq.Set(SPR_ROTOR_STOPPED);
w->sprite_cache.sprite_seq.Set(SPR_ROTOR_STOPPED);
w->random_bits = VehicleRandomBits();
/* Use rotor's air.state to store the rotor animation frame */
w->state = HRS_ROTOR_STOPPED;
@ -497,7 +497,7 @@ static void HelicopterTickHandler(Aircraft *v)
if (spd == 0) {
u->state = HRS_ROTOR_STOPPED;
GetRotorImage(v, EIT_ON_MAP, &seq);
if (u->sprite_seq == seq) return;
if (u->sprite_cache.sprite_seq == seq) return;
} else if (tick >= spd) {
u->tick_counter = 0;
u->state++;
@ -507,7 +507,7 @@ static void HelicopterTickHandler(Aircraft *v)
return;
}
u->sprite_seq = seq;
u->sprite_cache.sprite_seq = seq;
u->UpdatePositionAndViewport();
}
@ -528,7 +528,7 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z)
v->UpdatePosition();
v->UpdateViewport(true, false);
if (v->subtype == AIR_HELICOPTER) {
GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_seq);
GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_cache.sprite_seq);
}
Aircraft *u = v->Next();
@ -540,7 +540,7 @@ void SetAircraftPosition(Aircraft *v, int x, int y, int z)
safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
u->z_pos = GetSlopePixelZ(safe_x, safe_y);
u->sprite_seq.CopyWithoutPalette(v->sprite_seq); // the shadow is never coloured
u->sprite_cache.sprite_seq.CopyWithoutPalette(v->sprite_cache.sprite_seq); // the shadow is never coloured
u->UpdatePositionAndViewport();
@ -1278,7 +1278,7 @@ void Aircraft::MarkDirty()
this->colourmap = PAL_NONE;
this->UpdateViewport(true, false);
if (this->subtype == AIR_HELICOPTER) {
GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_seq);
GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_cache.sprite_seq);
}
}

View File

@ -439,7 +439,7 @@ void AddArticulatedParts(Vehicle *first)
v->max_age = 0;
v->engine_type = engine_type;
v->value = 0;
v->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
if (flip_image) v->spritenum++;

View File

@ -110,7 +110,7 @@ void DisasterVehicle::UpdateImage()
{
SpriteID img = this->image_override;
if (img == 0) img = _disaster_images[this->subtype][this->direction];
this->sprite_seq.Set(img);
this->sprite_cache.sprite_seq.Set(img);
}
/**
@ -498,7 +498,7 @@ static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
v->tick_counter++;
if (HasBit(v->tick_counter, 0)) return true;
SpriteID &cur_image = v->sprite_seq.seq[0].sprite;
SpriteID &cur_image = v->sprite_cache.sprite_seq.seq[0].sprite;
if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1;
v->UpdatePositionAndViewport();

View File

@ -28,8 +28,8 @@
*/
static bool IncrementSprite(EffectVehicle *v, SpriteID last)
{
if (v->sprite_seq.seq[0].sprite != last) {
v->sprite_seq.seq[0].sprite++;
if (v->sprite_cache.sprite_seq.seq[0].sprite != last) {
v->sprite_cache.sprite_seq.seq[0].sprite++;
return true;
} else {
return false;
@ -39,7 +39,7 @@ static bool IncrementSprite(EffectVehicle *v, SpriteID last)
static void ChimneySmokeInit(EffectVehicle *v)
{
uint32 r = Random();
v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
v->progress = GB(r, 16, 3);
}
@ -55,7 +55,7 @@ static bool ChimneySmokeTick(EffectVehicle *v)
}
if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
}
v->progress = 7;
v->UpdatePositionAndViewport();
@ -66,7 +66,7 @@ static bool ChimneySmokeTick(EffectVehicle *v)
static void SteamSmokeInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_STEAM_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_STEAM_SMOKE_0);
v->progress = 12;
}
@ -96,7 +96,7 @@ static bool SteamSmokeTick(EffectVehicle *v)
static void DieselSmokeInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_DIESEL_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_DIESEL_SMOKE_0);
v->progress = 0;
}
@ -120,7 +120,7 @@ static bool DieselSmokeTick(EffectVehicle *v)
static void ElectricSparkInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
v->sprite_cache.sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
v->progress = 1;
}
@ -143,7 +143,7 @@ static bool ElectricSparkTick(EffectVehicle *v)
static void SmokeInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_SMOKE_0);
v->progress = 12;
}
@ -173,7 +173,7 @@ static bool SmokeTick(EffectVehicle *v)
static void ExplosionLargeInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
v->progress = 0;
}
@ -193,7 +193,7 @@ static bool ExplosionLargeTick(EffectVehicle *v)
static void BreakdownSmokeInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
v->progress = 0;
}
@ -202,7 +202,7 @@ static bool BreakdownSmokeTick(EffectVehicle *v)
v->progress++;
if ((v->progress & 7) == 0) {
if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) {
v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
}
v->UpdatePositionAndViewport();
}
@ -218,7 +218,7 @@ static bool BreakdownSmokeTick(EffectVehicle *v)
static void ExplosionSmallInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
v->progress = 0;
}
@ -238,7 +238,7 @@ static bool ExplosionSmallTick(EffectVehicle *v)
static void BulldozerInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_BULLDOZER_NE);
v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE);
v->progress = 0;
v->animation_state = 0;
v->animation_substate = 0;
@ -289,7 +289,7 @@ static bool BulldozerTick(EffectVehicle *v)
if ((v->progress & 7) == 0) {
const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
v->sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
v->x_pos += _inc_by_dir[b->direction].x;
v->y_pos += _inc_by_dir[b->direction].y;
@ -311,7 +311,7 @@ static bool BulldozerTick(EffectVehicle *v)
static void BubbleInit(EffectVehicle *v)
{
v->sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
v->spritenum = 0;
v->progress = 0;
}
@ -474,8 +474,8 @@ static bool BubbleTick(EffectVehicle *v)
if ((v->progress & 3) != 0) return true;
if (v->spritenum == 0) {
v->sprite_seq.seq[0].sprite++;
if (v->sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
v->sprite_cache.sprite_seq.seq[0].sprite++;
if (v->sprite_cache.sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
v->UpdatePositionAndViewport();
return true;
}
@ -520,7 +520,7 @@ static bool BubbleTick(EffectVehicle *v)
v->x_pos += b->x;
v->y_pos += b->y;
v->z_pos += b->z;
v->sprite_seq.Set(SPR_BUBBLE_0 + b->image);
v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_0 + b->image);
v->UpdatePositionAndViewport();

View File

@ -301,7 +301,7 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin
v->date_of_last_service = _date;
v->build_year = _cur_year;
v->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
v->SetFrontEngine();

View File

@ -1153,7 +1153,7 @@ static const OldChunks vehicle_chunk[] = {
OCL_SVAR( OC_UINT8, Vehicle, owner ),
OCL_SVAR( OC_TILE, Vehicle, tile ),
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_seq.seq[0].sprite ),
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_cache.sprite_seq.seq[0].sprite ),
OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically
@ -1246,7 +1246,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
if (v == nullptr) continue;
v->refit_cap = v->cargo_cap;
SpriteID sprite = v->sprite_seq.seq[0].sprite;
SpriteID sprite = v->sprite_cache.sprite_seq.seq[0].sprite;
/* no need to override other sprites */
if (IsInsideMM(sprite, 1460, 1465)) {
sprite += 580; // aircraft smoke puff
@ -1257,7 +1257,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
} else if (IsInsideMM(sprite, 2516, 2539)) {
sprite += 1385; // rotor or disaster-related vehicles
}
v->sprite_seq.seq[0].sprite = sprite;
v->sprite_cache.sprite_seq.seq[0].sprite = sprite;
switch (v->type) {
case VEH_TRAIN: {

View File

@ -445,21 +445,21 @@ void AfterLoadVehicles(bool part_of_load)
case VEH_ROAD:
case VEH_TRAIN:
case VEH_SHIP:
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq);
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_cache.sprite_seq);
break;
case VEH_AIRCRAFT:
if (Aircraft::From(v)->IsNormalAircraft()) {
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq);
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_cache.sprite_seq);
/* The plane's shadow will have the same image as the plane, but no colour */
Vehicle *shadow = v->Next();
shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq);
shadow->sprite_cache.sprite_seq.CopyWithoutPalette(v->sprite_cache.sprite_seq);
/* In the case of a helicopter we will update the rotor sprites */
if (v->subtype == AIR_HELICOPTER) {
Vehicle *rotor = shadow->Next();
GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_seq);
GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_cache.sprite_seq);
}
UpdateAircraftCache(Aircraft::From(v), true);
@ -808,7 +808,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164),
SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, SLV_164, SL_MAX_VERSION),
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
SLE_VAR(Vehicle, sprite_cache.sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
SLE_CONDNULL(5, SL_MIN_VERSION, SLV_59),
SLE_VAR(Vehicle, progress, SLE_UINT8),
SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
@ -848,7 +848,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, SLV_5, SL_MAX_VERSION),
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
SLE_VAR(Vehicle, sprite_cache.sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLE_CONDVAR(Vehicle, age, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_VAR(Vehicle, tick_counter, SLE_UINT8),

View File

@ -864,7 +864,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, u
v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships);
v->date_of_last_service = _date;
v->build_year = _cur_year;
v->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
v->UpdateCache();

View File

@ -624,7 +624,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const
v->date_of_last_service = _date;
v->build_year = _cur_year;
v->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
v->group_id = DEFAULT_GROUP;
@ -690,7 +690,7 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
u->engine_type = v->engine_type;
u->date_of_last_service = v->date_of_last_service;
u->build_year = v->build_year;
u->sprite_seq.Set(SPR_IMG_QUERY);
u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
u->random_bits = VehicleRandomBits();
v->SetMultiheaded();
u->SetMultiheaded();
@ -756,7 +756,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin
v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
v->date_of_last_service = _date;
v->build_year = _cur_year;
v->sprite_seq.Set(SPR_IMG_QUERY);
v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
v->random_bits = VehicleRandomBits();
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);

View File

@ -1094,25 +1094,19 @@ static void DoDrawVehicle(const Vehicle *v)
/*
* If the vehicle sprite was not updated despite further viewport changes, we need
* to update it before drawing.
*
* I'm not keen on casting to mutable - it's the approach JGR uses in JGRPP but I
* wonder if there's a cleaner option (even though we can only take the decision
* whether to update once we already know the vehicle is going to appear in a
* viewport)
*/
if (v->rstate.sprite_has_viewport_changes) {
Vehicle* v_mutable = const_cast<Vehicle*>(v);
if (v->sprite_cache.sprite_has_viewport_changes) {
VehicleSpriteSeq seq;
v_mutable->GetImage(v_mutable->direction, EIT_ON_MAP, &seq);
v_mutable->sprite_seq = seq;
v_mutable->rstate.sprite_has_viewport_changes = false;
v->GetImage(v->direction, EIT_ON_MAP, &seq);
v->sprite_cache.sprite_seq = seq;
v->sprite_cache.sprite_has_viewport_changes = false;
}
StartSpriteCombine();
for (uint i = 0; i < v->sprite_seq.count; ++i) {
PaletteID pal2 = v->sprite_seq.seq[i].pal;
for (uint i = 0; i < v->sprite_cache.sprite_seq.count; ++i) {
PaletteID pal2 = v->sprite_cache.sprite_seq.seq[i].pal;
if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
AddSortableSpriteToDraw(v->sprite_cache.sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
}
EndSpriteCombine();
@ -1175,10 +1169,10 @@ void ViewportAddVehicles(DrawPixelInfo *dpi)
* are part of a newgrf vehicle set which changes bounding boxes within a
* single vehicle direction.
*
* TODO: is there a cleaner solution than casting to a mutable type?
* TODO: this will consider too many false positives, use the bounding box
* information or something which better narrows down the candidates.
*/
Vehicle* v_mutable = const_cast<Vehicle*>(v);
v_mutable->rstate.is_viewport_candidate = true;
v->sprite_cache.is_viewport_candidate = true;
}
v = v->hash_viewport_next;
@ -1605,7 +1599,7 @@ void Vehicle::UpdatePosition()
void Vehicle::UpdateViewport(bool dirty)
{
Rect new_coord;
this->sprite_seq.GetBounds(&new_coord);
this->sprite_cache.sprite_seq.GetBounds(&new_coord);
Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
new_coord.left += pt.x;

View File

@ -124,13 +124,6 @@ struct VehicleCache {
byte cached_vis_effect; ///< Visual effect to show (see #VisualEffect)
};
/** Values for controlling how a vehicle's sprites are refreshed */
struct VehicleSpriteRefreshState {
Direction last_direction; ///< Last direction we obtained sprites for
bool is_viewport_candidate; ///< The vehicle has been in the hash for a shown viewport recently
bool sprite_has_viewport_changes; ///< There have been viewport changes since the sprite was last updated
};
/** Sprite sequence for a vehicle part. */
struct VehicleSpriteSeq {
PalSpriteID seq[4];
@ -188,6 +181,17 @@ struct VehicleSpriteSeq {
void Draw(int x, int y, PaletteID default_pal, bool force_pal) const;
};
/**
* Cache for vehicle sprites and values relating to whether they should be updated before drawing,
* or calculating the viewport.
*/
struct MutableSpriteCache {
Direction last_direction; ///< Last direction we obtained sprites for
mutable bool is_viewport_candidate; ///< The vehicle has been in the hash for a shown viewport recently
mutable bool sprite_has_viewport_changes; ///< There have been viewport changes since the sprite was last updated
mutable VehicleSpriteSeq sprite_seq; ///< Vehicle appearance.
};
/** A vehicle pool for a little over 1 million vehicles. */
typedef Pool<Vehicle, VehicleID, 512, 0xFF000> VehiclePool;
extern VehiclePool _vehicle_pool;
@ -282,7 +286,6 @@ public:
* 0xff == reserved for another custom sprite
*/
byte spritenum;
VehicleSpriteSeq sprite_seq; ///< Vehicle appearance.
byte x_extent; ///< x-extent of vehicle bounding box
byte y_extent; ///< y-extent of vehicle bounding box
byte z_extent; ///< z-extent of vehicle bounding box
@ -334,7 +337,7 @@ public:
NewGRFCache grf_cache; ///< Cache of often used calculated NewGRF values
VehicleCache vcache; ///< Cache of often used vehicle values.
VehicleSpriteRefreshState rstate; ///< Values relating to whether sprites should be refreshed, see #VehicleSpriteRefreshState
MutableSpriteCache sprite_cache; ///< Cache of sprites and values related to recalculating them, see #MutableSpriteCache
Vehicle(VehicleType type = VEH_INVALID);
@ -1044,7 +1047,7 @@ struct SpecializedVehicle : public Vehicle {
*/
inline SpecializedVehicle<T, Type>() : Vehicle(Type)
{
this->sprite_seq.count = 1;
this->sprite_cache.sprite_seq.count = 1;
}
/**
@ -1193,24 +1196,24 @@ struct SpecializedVehicle : public Vehicle {
* there won't be enough change in bounding box or offsets to need
* to resolve a new sprite.
*/
if (this->direction != this->rstate.last_direction || this->rstate.is_viewport_candidate) {
if (this->direction != this->sprite_cache.last_direction || this->sprite_cache.is_viewport_candidate) {
VehicleSpriteSeq seq;
((T*)this)->T::GetImage(this->direction, EIT_ON_MAP, &seq);
if (this->sprite_seq != seq) {
if (this->sprite_cache.sprite_seq != seq) {
sprite_has_changed = true;
this->sprite_seq = seq;
this->sprite_cache.sprite_seq = seq;
}
this->rstate.last_direction = this->direction;
this->rstate.is_viewport_candidate = false;
this->rstate.sprite_has_viewport_changes = false;
this->sprite_cache.last_direction = this->direction;
this->sprite_cache.is_viewport_candidate = false;
this->sprite_cache.sprite_has_viewport_changes = false;
} else {
/*
* Changes could still be relevant when we render the vehicle even if
* they don't alter the bounding box
*/
this->rstate.sprite_has_viewport_changes = true;
this->sprite_cache.sprite_has_viewport_changes = true;
}
if (force_update || sprite_has_changed) {