mirror of https://github.com/OpenTTD/OpenTTD
(svn r20627) -Codechange: unify the animation code of station, airport, house and industry tiles
parent
d3f57f0e4d
commit
ddf1510a23
|
@ -28,6 +28,7 @@
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
#include "table/airporttiles.h"
|
#include "table/airporttiles.h"
|
||||||
#include "date_func.h"
|
#include "date_func.h"
|
||||||
|
#include "newgrf_animation_base.h"
|
||||||
|
|
||||||
|
|
||||||
AirportTileSpec AirportTileSpec::tiles[NUM_AIRPORTTILES];
|
AirportTileSpec AirportTileSpec::tiles[NUM_AIRPORTTILES];
|
||||||
|
@ -312,96 +313,29 @@ bool DrawNewAirportTile(TileInfo *ti, Station *st, StationGfx gfx, const Airport
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Helper class for animation control. */
|
||||||
|
struct AirportTileAnimationBase : public AnimationBase<AirportTileAnimationBase, AirportTileSpec, Station, GetAirportTileCallback> {
|
||||||
|
static const CallbackID cb_animation_speed = CBID_AIRPTILE_ANIMATION_SPEED;
|
||||||
|
static const CallbackID cb_animation_next_frame = CBID_AIRPTILE_ANIM_NEXT_FRAME;
|
||||||
|
|
||||||
|
static const AirportTileCallbackMask cbm_animation_speed = CBM_AIRT_ANIM_SPEED;
|
||||||
|
static const AirportTileCallbackMask cbm_animation_next_frame = CBM_AIRT_ANIM_NEXT_FRAME;
|
||||||
|
};
|
||||||
|
|
||||||
void AnimateAirportTile(TileIndex tile)
|
void AnimateAirportTile(TileIndex tile)
|
||||||
{
|
{
|
||||||
Station *st = Station::GetByTile(tile);
|
|
||||||
const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
|
const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
|
||||||
uint8 animation_speed = ats->animation.speed;
|
if (ats == NULL) return;
|
||||||
|
|
||||||
if (HasBit(ats->callback_mask, CBM_AIRT_ANIM_SPEED)) {
|
AirportTileAnimationBase::AnimateTile(ats, Station::GetByTile(tile), tile, HasBit(ats->animation_special_flags, 0));
|
||||||
uint16 callback_res = GetAirportTileCallback(CBID_AIRPTILE_ANIMATION_SPEED, 0, 0, ats, st, tile);
|
|
||||||
if (callback_res != CALLBACK_FAILED) animation_speed = Clamp(callback_res & 0xFF, 0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* An animation speed of 2 means the animation frame changes 4 ticks, and
|
|
||||||
* increasing this value by one doubles the wait. 0 is the minimum value
|
|
||||||
* allowed for animation_speed, which corresponds to 30ms, and 16 is the
|
|
||||||
* maximum, corresponding to around 33 minutes. */
|
|
||||||
if ((_tick_counter % (1 << animation_speed)) != 0) return;
|
|
||||||
|
|
||||||
bool frame_set_by_callback = false;
|
|
||||||
uint8 frame = GetAnimationFrame(tile);
|
|
||||||
uint16 num_frames = ats->animation.frames;
|
|
||||||
|
|
||||||
if (HasBit(ats->callback_mask, CBM_AIRT_ANIM_NEXT_FRAME)) {
|
|
||||||
uint16 callback_res = GetAirportTileCallback(CBID_AIRPTILE_ANIM_NEXT_FRAME, HasBit(ats->animation_special_flags, 0) ? Random() : 0, 0, ats, st, tile);
|
|
||||||
|
|
||||||
if (callback_res != CALLBACK_FAILED) {
|
|
||||||
frame_set_by_callback = true;
|
|
||||||
|
|
||||||
switch (callback_res & 0xFF) {
|
|
||||||
case 0xFF:
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
/* Carry on as normal. */
|
|
||||||
frame_set_by_callback = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
frame = callback_res & 0xFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_res, 8, 7) != 0) PlayTileSound(ats->grf_prop.grffile, GB(callback_res, 8, 7), tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame_set_by_callback) {
|
|
||||||
if (frame < num_frames) {
|
|
||||||
frame++;
|
|
||||||
} else if (frame == num_frames && ats->animation.status == ANIM_STATUS_LOOPING) {
|
|
||||||
/* This animation loops, so start again from the beginning */
|
|
||||||
frame = 0;
|
|
||||||
} else {
|
|
||||||
/* This animation doesn't loop, so stay here */
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAnimationFrame(tile, frame);
|
|
||||||
MarkTileDirtyByTile(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ChangeAirportTileAnimationFrame(const AirportTileSpec *ats, TileIndex tile, AirpAnimationTrigger trigger, Station *st)
|
|
||||||
{
|
|
||||||
uint16 callback_res = GetAirportTileCallback(CBID_AIRPTILE_ANIM_START_STOP, Random(), trigger, ats, st, tile);
|
|
||||||
if (callback_res == CALLBACK_FAILED) return;
|
|
||||||
|
|
||||||
switch (callback_res & 0xFF) {
|
|
||||||
case 0xFD: /* Do nothing. */ break;
|
|
||||||
case 0xFE: AddAnimatedTile(tile); break;
|
|
||||||
case 0xFF: DeleteAnimatedTile(tile); break;
|
|
||||||
default:
|
|
||||||
SetAnimationFrame(tile, callback_res & 0xFF);
|
|
||||||
AddAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_res, 8, 7) != 0) PlayTileSound(ats->grf_prop.grffile, GB(callback_res, 8, 7), tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AirportTileAnimationTrigger(Station *st, TileIndex tile, AirpAnimationTrigger trigger, CargoID cargo_type)
|
void AirportTileAnimationTrigger(Station *st, TileIndex tile, AirpAnimationTrigger trigger, CargoID cargo_type)
|
||||||
{
|
{
|
||||||
const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
|
const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
|
||||||
|
|
||||||
if (!HasBit(ats->animation.triggers, trigger)) return;
|
if (!HasBit(ats->animation.triggers, trigger)) return;
|
||||||
|
|
||||||
ChangeAirportTileAnimationFrame(ats, tile, trigger, st);
|
AirportTileAnimationBase::ChangeAnimationFrame(CBID_AIRPTILE_ANIM_START_STOP, ats, st, tile, Random(), (uint8)trigger | (cargo_type << 8));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AirportAnimationTrigger(Station *st, AirpAnimationTrigger trigger, CargoID cargo_type)
|
void AirportAnimationTrigger(Station *st, AirpAnimationTrigger trigger, CargoID cargo_type)
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 newgrf_animation_base.h Function implementations related to NewGRF animation. */
|
||||||
|
|
||||||
|
/* No inclusion guards as this file must only be included from .cpp files. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for a unified approach to NewGRF animation.
|
||||||
|
* @tparam Tbase Instantiation of this class.
|
||||||
|
* @tparam Tspec NewGRF specification related to the animated tile.
|
||||||
|
* @tparam Tobj Object related to the animated tile.
|
||||||
|
* @tparam GetCallback The callback function pointer.
|
||||||
|
*/
|
||||||
|
template <typename Tbase, typename Tspec, typename Tobj, uint16 (*GetCallback)(CallbackID callback, uint32 param1, uint32 param2, const Tspec *statspec, const Tobj *st, TileIndex tile)>
|
||||||
|
struct AnimationBase {
|
||||||
|
/**
|
||||||
|
* Animate a single tile.
|
||||||
|
* @param cb The callback to actually call.
|
||||||
|
* @param spec Specification related to the tile.
|
||||||
|
* @param obj Object related to the tile.
|
||||||
|
* @param tile Tile to animate changes for.
|
||||||
|
* @param random_animation Whether to pass random bits to the "next frame" callback.
|
||||||
|
*/
|
||||||
|
static void AnimateTile(const Tspec *spec, const Tobj *obj, TileIndex tile, bool random_animation)
|
||||||
|
{
|
||||||
|
assert(spec != NULL);
|
||||||
|
|
||||||
|
/* Acquire the animation speed from the NewGRF. */
|
||||||
|
uint8 animation_speed = spec->animation.speed;
|
||||||
|
if (HasBit(spec->callback_mask, Tbase::cbm_animation_speed)) {
|
||||||
|
uint16 callback = GetCallback(Tbase::cb_animation_speed, 0, 0, spec, obj, tile);
|
||||||
|
if (callback != CALLBACK_FAILED) animation_speed = Clamp(callback & 0xFF, 0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An animation speed of 2 means the animation frame changes 4 ticks, and
|
||||||
|
* increasing this value by one doubles the wait. 0 is the minimum value
|
||||||
|
* allowed for animation_speed, which corresponds to 30ms, and 16 is the
|
||||||
|
* maximum, corresponding to around 33 minutes. */
|
||||||
|
if (_tick_counter % (1 << animation_speed) != 0) return;
|
||||||
|
|
||||||
|
uint8 frame = GetAnimationFrame(tile);
|
||||||
|
uint8 num_frames = spec->animation.frames;
|
||||||
|
|
||||||
|
bool frame_set_by_callback = false;
|
||||||
|
|
||||||
|
if (HasBit(spec->callback_mask, Tbase::cbm_animation_next_frame)) {
|
||||||
|
uint16 callback = GetCallback(Tbase::cb_animation_next_frame, random_animation ? Random() : 0, 0, spec, obj, tile);
|
||||||
|
|
||||||
|
if (callback != CALLBACK_FAILED) {
|
||||||
|
frame_set_by_callback = true;
|
||||||
|
|
||||||
|
switch (callback & 0xFF) {
|
||||||
|
case 0xFF:
|
||||||
|
DeleteAnimatedTile(tile);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xFE:
|
||||||
|
frame_set_by_callback = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
frame = callback & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the lower 7 bits of the upper byte of the callback
|
||||||
|
* result are not empty, it is a sound effect. */
|
||||||
|
if (GB(callback, 8, 7) != 0) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame_set_by_callback) {
|
||||||
|
if (frame < num_frames) {
|
||||||
|
frame++;
|
||||||
|
} else if (frame == num_frames && spec->animation.status == ANIM_STATUS_LOOPING) {
|
||||||
|
/* This animation loops, so start again from the beginning */
|
||||||
|
frame = 0;
|
||||||
|
} else {
|
||||||
|
/* This animation doesn't loop, so stay here */
|
||||||
|
DeleteAnimatedTile(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAnimationFrame(tile, frame);
|
||||||
|
MarkTileDirtyByTile(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a callback to determine what the next animation step is and
|
||||||
|
* execute that step. This includes stopping and starting animations
|
||||||
|
* as well as updating animation frames and playing sounds.
|
||||||
|
* @param cb The callback to actually call.
|
||||||
|
* @param spec Specification related to the tile.
|
||||||
|
* @param obj Object related to the tile.
|
||||||
|
* @param tile Tile to consider animation changes for.
|
||||||
|
* @param random_bits Random bits for this update. To be passed as parameter to the NewGRF.
|
||||||
|
* @param trigger What triggered this update? To be passed as parameter to the NewGRF.
|
||||||
|
*/
|
||||||
|
static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, const Tobj *obj, TileIndex tile, uint32 random_bits, uint32 trigger)
|
||||||
|
{
|
||||||
|
uint16 callback = GetCallback(cb, random_bits, trigger, spec, obj, tile);
|
||||||
|
if (callback == CALLBACK_FAILED) return;
|
||||||
|
|
||||||
|
switch (callback & 0xFF) {
|
||||||
|
case 0xFD: /* Do nothing. */ break;
|
||||||
|
case 0xFE: AddAnimatedTile(tile); break;
|
||||||
|
case 0xFF: DeleteAnimatedTile(tile); break;
|
||||||
|
default:
|
||||||
|
SetAnimationFrame(tile, callback);
|
||||||
|
AddAnimatedTile(tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the lower 7 bits of the upper byte of the callback
|
||||||
|
* result are not empty, it is a sound effect. */
|
||||||
|
if (GB(callback, 8, 7) != 0) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
|
||||||
|
}
|
||||||
|
};
|
|
@ -28,6 +28,7 @@
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "genworld.h"
|
#include "genworld.h"
|
||||||
#include "date_func.h"
|
#include "date_func.h"
|
||||||
|
#include "newgrf_animation_base.h"
|
||||||
|
|
||||||
static BuildingCounts<uint32> _building_counts;
|
static BuildingCounts<uint32> _building_counts;
|
||||||
static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX];
|
static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX];
|
||||||
|
@ -471,82 +472,36 @@ void DrawNewHouseTile(TileInfo *ti, HouseID house_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Simple wrapper for GetHouseCallback to keep the animation unified. */
|
||||||
|
uint16 GetSimpleHouseCallback(CallbackID callback, uint32 param1, uint32 param2, const HouseSpec *spec, const Town *town, TileIndex tile)
|
||||||
|
{
|
||||||
|
return GetHouseCallback(callback, param1, param2, spec - HouseSpec::Get(0), town, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper class for animation control. */
|
||||||
|
struct HouseAnimationBase : public AnimationBase<HouseAnimationBase, HouseSpec, Town, GetSimpleHouseCallback> {
|
||||||
|
static const CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED;
|
||||||
|
static const CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME;
|
||||||
|
|
||||||
|
static const HouseCallbackMask cbm_animation_speed = CBM_HOUSE_ANIMATION_SPEED;
|
||||||
|
static const HouseCallbackMask cbm_animation_next_frame = CBM_HOUSE_ANIMATION_NEXT_FRAME;
|
||||||
|
};
|
||||||
|
|
||||||
void AnimateNewHouseTile(TileIndex tile)
|
void AnimateNewHouseTile(TileIndex tile)
|
||||||
{
|
{
|
||||||
const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
|
const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
|
||||||
byte animation_speed = hs->animation.speed;
|
if (hs == NULL) return;
|
||||||
bool frame_set_by_callback = false;
|
|
||||||
|
|
||||||
if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_SPEED)) {
|
HouseAnimationBase::AnimateTile(hs, Town::GetByTile(tile), tile, HasBit(hs->extra_flags, CALLBACK_1A_RANDOM_BITS));
|
||||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_SPEED, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
|
|
||||||
if (callback_res != CALLBACK_FAILED) animation_speed = Clamp(callback_res & 0xFF, 2, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* An animation speed of 2 means the animation frame changes 4 ticks, and
|
|
||||||
* increasing this value by one doubles the wait. 2 is the minimum value
|
|
||||||
* allowed for animation_speed, which corresponds to 120ms, and 16 is the
|
|
||||||
* maximum, corresponding to around 33 minutes. */
|
|
||||||
if (_tick_counter % (1 << animation_speed) != 0) return;
|
|
||||||
|
|
||||||
byte frame = GetAnimationFrame(tile);
|
|
||||||
byte num_frames = hs->animation.frames;
|
|
||||||
|
|
||||||
if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_NEXT_FRAME)) {
|
|
||||||
uint32 param = (hs->extra_flags & CALLBACK_1A_RANDOM_BITS) ? Random() : 0;
|
|
||||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_NEXT_FRAME, param, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
|
|
||||||
|
|
||||||
if (callback_res != CALLBACK_FAILED) {
|
|
||||||
frame_set_by_callback = true;
|
|
||||||
|
|
||||||
switch (callback_res & 0xFF) {
|
|
||||||
case 0xFF:
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
/* Carry on as normal. */
|
|
||||||
frame_set_by_callback = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
frame = callback_res & 0xFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_res, 8, 7) != 0) PlayTileSound(hs->grf_prop.grffile, GB(callback_res, 8, 7), tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame_set_by_callback) {
|
|
||||||
if (frame < num_frames) {
|
|
||||||
frame++;
|
|
||||||
} else if (frame == num_frames && hs->animation.status == ANIM_STATUS_LOOPING) {
|
|
||||||
/* This animation loops, so start again from the beginning */
|
|
||||||
frame = 0;
|
|
||||||
} else {
|
|
||||||
/* This animation doesn't loop, so stay here */
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAnimationFrame(tile, frame);
|
|
||||||
MarkTileDirtyByTile(tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeHouseAnimationFrame(const GRFFile *file, TileIndex tile, uint16 callback_result)
|
void AnimateNewHouseConstruction(TileIndex tile)
|
||||||
{
|
{
|
||||||
switch (callback_result & 0xFF) {
|
const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
|
||||||
case 0xFD: /* Do nothing. */ break;
|
|
||||||
case 0xFE: AddAnimatedTile(tile); break;
|
if (HasBit(hs->callback_mask, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE)) {
|
||||||
case 0xFF: DeleteAnimatedTile(tile); break;
|
HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, hs, Town::GetByTile(tile), tile, 0, 0);
|
||||||
default:
|
|
||||||
SetAnimationFrame(tile, callback_result & 0xFF);
|
|
||||||
AddAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_result, 8, 7) != 0) PlayTileSound(file, GB(callback_result, 8, 7), tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanDeleteHouse(TileIndex tile)
|
bool CanDeleteHouse(TileIndex tile)
|
||||||
|
@ -573,9 +528,7 @@ static void AnimationControl(TileIndex tile, uint16 random_bits)
|
||||||
|
|
||||||
if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) {
|
if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) {
|
||||||
uint32 param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random();
|
uint32 param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random();
|
||||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_START_STOP, param, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
|
HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_ANIMATION_START_STOP, hs, Town::GetByTile(tile), tile, param, 0);
|
||||||
|
|
||||||
if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(hs->grf_prop.grffile, tile, callback_res);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ void DecreaseBuildingCount(Town *t, HouseID house_id);
|
||||||
|
|
||||||
void DrawNewHouseTile(TileInfo *ti, HouseID house_id);
|
void DrawNewHouseTile(TileInfo *ti, HouseID house_id);
|
||||||
void AnimateNewHouseTile(TileIndex tile);
|
void AnimateNewHouseTile(TileIndex tile);
|
||||||
void ChangeHouseAnimationFrame(const struct GRFFile *file, TileIndex tile, uint16 callback_result);
|
void AnimateNewHouseConstruction(TileIndex tile);
|
||||||
|
|
||||||
uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, const Town *town, TileIndex tile, bool not_yet_constructed = false, uint8 initial_random_bits = 0);
|
uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, const Town *town, TileIndex tile, bool not_yet_constructed = false, uint8 initial_random_bits = 0);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "water.h"
|
#include "water.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "date_func.h"
|
#include "date_func.h"
|
||||||
|
#include "newgrf_animation_base.h"
|
||||||
|
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
|
|
||||||
|
@ -297,99 +298,36 @@ CommandCost PerformIndustryTileSlopeCheck(TileIndex ind_base_tile, TileIndex ind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimateNewIndustryTile(TileIndex tile)
|
/* Simple wrapper for GetHouseCallback to keep the animation unified. */
|
||||||
|
uint16 GetSimpleIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, const IndustryTileSpec *spec, const Industry *ind, TileIndex tile)
|
||||||
{
|
{
|
||||||
Industry *ind = Industry::GetByTile(tile);
|
return GetIndustryTileCallback(callback, param1, param2, spec - GetIndustryTileSpec(0), const_cast<Industry *>(ind), tile);
|
||||||
IndustryGfx gfx = GetIndustryGfx(tile);
|
|
||||||
const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
|
|
||||||
byte animation_speed = itspec->animation.speed;
|
|
||||||
|
|
||||||
if (HasBit(itspec->callback_mask, CBM_INDT_ANIM_SPEED)) {
|
|
||||||
uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIMATION_SPEED, 0, 0, gfx, ind, tile);
|
|
||||||
if (callback_res != CALLBACK_FAILED) animation_speed = Clamp(callback_res & 0xFF, 0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* An animation speed of 2 means the animation frame changes 4 ticks, and
|
|
||||||
* increasing this value by one doubles the wait. 0 is the minimum value
|
|
||||||
* allowed for animation_speed, which corresponds to 30ms, and 16 is the
|
|
||||||
* maximum, corresponding to around 33 minutes. */
|
|
||||||
if ((_tick_counter % (1 << animation_speed)) != 0) return;
|
|
||||||
|
|
||||||
bool frame_set_by_callback = false;
|
|
||||||
byte frame = GetAnimationFrame(tile);
|
|
||||||
uint16 num_frames = itspec->animation.frames;
|
|
||||||
|
|
||||||
if (HasBit(itspec->callback_mask, CBM_INDT_ANIM_NEXT_FRAME)) {
|
|
||||||
uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIM_NEXT_FRAME,
|
|
||||||
(itspec->special_flags & INDTILE_SPECIAL_NEXTFRAME_RANDOMBITS) ? Random() : 0, 0, gfx, ind, tile);
|
|
||||||
|
|
||||||
if (callback_res != CALLBACK_FAILED) {
|
|
||||||
frame_set_by_callback = true;
|
|
||||||
|
|
||||||
switch (callback_res & 0xFF) {
|
|
||||||
case 0xFF:
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
/* Carry on as normal. */
|
|
||||||
frame_set_by_callback = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
frame = callback_res & 0xFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_res, 8, 7) != 0) PlayTileSound(itspec->grf_prop.grffile, GB(callback_res, 8, 7), tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame_set_by_callback) {
|
|
||||||
if (frame < num_frames) {
|
|
||||||
frame++;
|
|
||||||
} else if (frame == num_frames && itspec->animation.status == ANIM_STATUS_LOOPING) {
|
|
||||||
/* This animation loops, so start again from the beginning */
|
|
||||||
frame = 0;
|
|
||||||
} else {
|
|
||||||
/* This animation doesn't loop, so stay here */
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAnimationFrame(tile, frame);
|
|
||||||
MarkTileDirtyByTile(tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ChangeIndustryTileAnimationFrame(const IndustryTileSpec *itspec, TileIndex tile, IndustryAnimationTrigger iat, uint32 random_bits, IndustryGfx gfx, Industry *ind)
|
/** Helper class for animation control. */
|
||||||
|
struct IndustryAnimationBase : public AnimationBase<IndustryAnimationBase, IndustryTileSpec, Industry, GetSimpleIndustryCallback> {
|
||||||
|
static const CallbackID cb_animation_speed = CBID_INDTILE_ANIMATION_SPEED;
|
||||||
|
static const CallbackID cb_animation_next_frame = CBID_INDTILE_ANIM_NEXT_FRAME;
|
||||||
|
|
||||||
|
static const IndustryTileCallbackMask cbm_animation_speed = CBM_INDT_ANIM_SPEED;
|
||||||
|
static const IndustryTileCallbackMask cbm_animation_next_frame = CBM_INDT_ANIM_NEXT_FRAME;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AnimateNewIndustryTile(TileIndex tile)
|
||||||
{
|
{
|
||||||
uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIM_START_STOP, random_bits, iat, gfx, ind, tile);
|
const IndustryTileSpec *itspec = GetIndustryTileSpec(GetIndustryGfx(tile));
|
||||||
if (callback_res == CALLBACK_FAILED) return;
|
if (itspec == NULL) return;
|
||||||
|
|
||||||
switch (callback_res & 0xFF) {
|
IndustryAnimationBase::AnimateTile(itspec, Industry::GetByTile(tile), tile, (itspec->special_flags & INDTILE_SPECIAL_NEXTFRAME_RANDOMBITS) != 0);
|
||||||
case 0xFD: /* Do nothing. */ break;
|
|
||||||
case 0xFE: AddAnimatedTile(tile); break;
|
|
||||||
case 0xFF: DeleteAnimatedTile(tile); break;
|
|
||||||
default:
|
|
||||||
SetAnimationFrame(tile, callback_res & 0xFF);
|
|
||||||
AddAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback_res, 8, 7) != 0) PlayTileSound(itspec->grf_prop.grffile, GB(callback_res, 8, 7), tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartStopIndustryTileAnimation(TileIndex tile, IndustryAnimationTrigger iat, uint32 random)
|
bool StartStopIndustryTileAnimation(TileIndex tile, IndustryAnimationTrigger iat, uint32 random)
|
||||||
{
|
{
|
||||||
IndustryGfx gfx = GetIndustryGfx(tile);
|
const IndustryTileSpec *itspec = GetIndustryTileSpec(GetIndustryGfx(tile));
|
||||||
const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
|
|
||||||
|
|
||||||
if (!HasBit(itspec->animation.triggers, iat)) return false;
|
if (!HasBit(itspec->animation.triggers, iat)) return false;
|
||||||
|
|
||||||
Industry *ind = Industry::GetByTile(tile);
|
IndustryAnimationBase::ChangeAnimationFrame(CBID_INDTILE_ANIM_START_STOP, itspec, Industry::GetByTile(tile), tile, random, iat);
|
||||||
ChangeIndustryTileAnimationFrame(itspec, tile, iat, random, gfx, ind);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "tunnelbridge_map.h"
|
#include "tunnelbridge_map.h"
|
||||||
#include "newgrf.h"
|
#include "newgrf.h"
|
||||||
#include "core/random_func.hpp"
|
#include "core/random_func.hpp"
|
||||||
|
#include "newgrf_animation_base.h"
|
||||||
#include "newgrf_class_func.h"
|
#include "newgrf_class_func.h"
|
||||||
|
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
|
@ -811,89 +812,21 @@ bool IsStationTileElectrifiable(TileIndex tile)
|
||||||
!HasBit(statspec->wires, GetStationGfx(tile));
|
!HasBit(statspec->wires, GetStationGfx(tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Helper class for animation control. */
|
||||||
|
struct StationAnimationBase : public AnimationBase<StationAnimationBase, StationSpec, BaseStation, GetStationCallback> {
|
||||||
|
static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED;
|
||||||
|
static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME;
|
||||||
|
|
||||||
|
static const StationCallbackMask cbm_animation_speed = CBM_STATION_ANIMATION_SPEED;
|
||||||
|
static const StationCallbackMask cbm_animation_next_frame = CBM_STATION_ANIMATION_NEXT_FRAME;
|
||||||
|
};
|
||||||
|
|
||||||
void AnimateStationTile(TileIndex tile)
|
void AnimateStationTile(TileIndex tile)
|
||||||
{
|
{
|
||||||
const StationSpec *ss = GetStationSpec(tile);
|
const StationSpec *ss = GetStationSpec(tile);
|
||||||
if (ss == NULL) return;
|
if (ss == NULL) return;
|
||||||
|
|
||||||
const BaseStation *st = BaseStation::GetByTile(tile);
|
StationAnimationBase::AnimateTile(ss, BaseStation::GetByTile(tile), tile, HasBit(ss->flags, SSF_CB141_RANDOM_BITS));
|
||||||
|
|
||||||
uint8 animation_speed = ss->animation.speed;
|
|
||||||
|
|
||||||
if (HasBit(ss->callback_mask, CBM_STATION_ANIMATION_SPEED)) {
|
|
||||||
uint16 callback = GetStationCallback(CBID_STATION_ANIMATION_SPEED, 0, 0, ss, st, tile);
|
|
||||||
if (callback != CALLBACK_FAILED) animation_speed = Clamp(callback & 0xFF, 0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_tick_counter % (1 << animation_speed) != 0) return;
|
|
||||||
|
|
||||||
uint8 frame = GetAnimationFrame(tile);
|
|
||||||
uint8 num_frames = ss->animation.frames;
|
|
||||||
|
|
||||||
bool frame_set_by_callback = false;
|
|
||||||
|
|
||||||
if (HasBit(ss->callback_mask, CBM_STATION_ANIMATION_NEXT_FRAME)) {
|
|
||||||
uint32 param = HasBit(ss->flags, SSF_CB141_RANDOM_BITS) ? Random() : 0;
|
|
||||||
uint16 callback = GetStationCallback(CBID_STATION_ANIM_NEXT_FRAME, param, 0, ss, st, tile);
|
|
||||||
|
|
||||||
if (callback != CALLBACK_FAILED) {
|
|
||||||
frame_set_by_callback = true;
|
|
||||||
|
|
||||||
switch (callback & 0xFF) {
|
|
||||||
case 0xFF:
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xFE:
|
|
||||||
frame_set_by_callback = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
frame = callback & 0xFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback, 8, 7) != 0) PlayTileSound(ss->grf_prop.grffile, GB(callback, 8, 7), tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame_set_by_callback) {
|
|
||||||
if (frame < num_frames) {
|
|
||||||
frame++;
|
|
||||||
} else if (frame == num_frames && ss->animation.status == ANIM_STATUS_LOOPING) {
|
|
||||||
/* This animation loops, so start again from the beginning */
|
|
||||||
frame = 0;
|
|
||||||
} else {
|
|
||||||
/* This animation doesn't loop, so stay here */
|
|
||||||
DeleteAnimatedTile(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAnimationFrame(tile, frame);
|
|
||||||
MarkTileDirtyByTile(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void ChangeStationAnimationFrame(const StationSpec *ss, const BaseStation *st, TileIndex tile, uint16 random_bits, StationAnimationTrigger trigger, CargoID cargo_type)
|
|
||||||
{
|
|
||||||
uint16 callback = GetStationCallback(CBID_STATION_ANIM_START_STOP, (random_bits << 16) | Random(), (uint8)trigger | (cargo_type << 8), ss, st, tile);
|
|
||||||
if (callback == CALLBACK_FAILED) return;
|
|
||||||
|
|
||||||
switch (callback & 0xFF) {
|
|
||||||
case 0xFD: /* Do nothing. */ break;
|
|
||||||
case 0xFE: AddAnimatedTile(tile); break;
|
|
||||||
case 0xFF: DeleteAnimatedTile(tile); break;
|
|
||||||
default:
|
|
||||||
SetAnimationFrame(tile, callback);
|
|
||||||
AddAnimatedTile(tile);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the lower 7 bits of the upper byte of the callback
|
|
||||||
* result are not empty, it is a sound effect. */
|
|
||||||
if (GB(callback, 8, 7) != 0) PlayTileSound(ss->grf_prop.grffile, GB(callback, 8, 7), tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriggerStationAnimation(const BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type)
|
void TriggerStationAnimation(const BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type)
|
||||||
|
@ -924,7 +857,7 @@ void TriggerStationAnimation(const BaseStation *st, TileIndex tile, StationAnima
|
||||||
} else {
|
} else {
|
||||||
cargo = GetReverseCargoTranslation(cargo_type, ss->grf_prop.grffile);
|
cargo = GetReverseCargoTranslation(cargo_type, ss->grf_prop.grffile);
|
||||||
}
|
}
|
||||||
ChangeStationAnimationFrame(ss, st, tile, random_bits, trigger, cargo);
|
StationAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, tile, (random_bits << 16) | Random(), (uint8)trigger | (cargo << 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,18 +411,12 @@ static void MakeSingleHouseBigger(TileIndex tile)
|
||||||
IncHouseConstructionTick(tile);
|
IncHouseConstructionTick(tile);
|
||||||
if (GetHouseConstructionTick(tile) != 0) return;
|
if (GetHouseConstructionTick(tile) != 0) return;
|
||||||
|
|
||||||
const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
|
AnimateNewHouseConstruction(tile);
|
||||||
|
|
||||||
/* Check and/or */
|
|
||||||
if (HasBit(hs->callback_mask, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE)) {
|
|
||||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
|
|
||||||
if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(hs->grf_prop.grffile, tile, callback_res);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsHouseCompleted(tile)) {
|
if (IsHouseCompleted(tile)) {
|
||||||
/* Now that construction is complete, we can add the population of the
|
/* Now that construction is complete, we can add the population of the
|
||||||
* building to the town. */
|
* building to the town. */
|
||||||
ChangePopulation(Town::GetByTile(tile), hs->population);
|
ChangePopulation(Town::GetByTile(tile), HouseSpec::Get(GetHouseType(tile))->population);
|
||||||
ResetHouseAge(tile);
|
ResetHouseAge(tile);
|
||||||
}
|
}
|
||||||
MarkTileDirtyByTile(tile);
|
MarkTileDirtyByTile(tile);
|
||||||
|
|
Loading…
Reference in New Issue