OpenTTD Source  20241108-master-g80f628063a
newgrf_animation_base.h
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
10 /* No inclusion guards as this file must only be included from .cpp files. */
11 
12 #include "animated_tile_func.h"
13 #include "core/random_func.hpp"
14 #include "timer/timer_game_tick.h"
15 #include "viewport_func.h"
16 #include "newgrf_animation_type.h"
17 #include "newgrf_callbacks.h"
18 #include "tile_map.h"
19 
20 template <typename Tobj>
22  static uint8_t Get(Tobj *, TileIndex tile) { return GetAnimationFrame(tile); }
23  static bool Set(Tobj *, TileIndex tile, uint8_t frame)
24  {
25  uint8_t prev_frame = GetAnimationFrame(tile);
26  if (prev_frame == frame) return false;
27 
28  SetAnimationFrame(tile, frame);
29  return true;
30  }
31 };
32 
42 template <typename Tbase, typename Tspec, typename Tobj, typename Textra, uint16_t (*GetCallback)(CallbackID callback, uint32_t param1, uint32_t param2, const Tspec *statspec, Tobj *st, TileIndex tile, Textra extra_data), typename Tframehelper>
43 struct AnimationBase {
52  static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data = 0)
53  {
54  assert(spec != nullptr);
55 
56  /* Acquire the animation speed from the NewGRF. */
57  uint8_t animation_speed = spec->animation.speed;
58  if (HasBit(spec->callback_mask, Tbase::cbm_animation_speed)) {
59  uint16_t callback = GetCallback(Tbase::cb_animation_speed, 0, 0, spec, obj, tile, extra_data);
60  if (callback != CALLBACK_FAILED) {
61  if (callback >= 0x100 && spec->grf_prop.grffile->grf_version >= 8) ErrorUnknownCallbackResult(spec->grf_prop.grffile->grfid, Tbase::cb_animation_speed, callback);
62  animation_speed = Clamp(callback & 0xFF, 0, 16);
63  }
64  }
65 
66  /* An animation speed of 2 means the animation frame changes 4 ticks, and
67  * increasing this value by one doubles the wait. 0 is the minimum value
68  * allowed for animation_speed, which corresponds to 30ms, and 16 is the
69  * maximum, corresponding to around 33 minutes. */
70  if (TimerGameTick::counter % (1ULL << animation_speed) != 0) return;
71 
72  uint8_t frame = Tframehelper::Get(obj, tile);
73  uint8_t num_frames = spec->animation.frames;
74 
75  bool frame_set_by_callback = false;
76 
77  if (HasBit(spec->callback_mask, Tbase::cbm_animation_next_frame)) {
78  uint16_t callback = GetCallback(Tbase::cb_animation_next_frame, random_animation ? Random() : 0, 0, spec, obj, tile, extra_data);
79 
80  if (callback != CALLBACK_FAILED) {
81  frame_set_by_callback = true;
82 
83  switch (callback & 0xFF) {
84  case 0xFF:
85  DeleteAnimatedTile(tile);
86  break;
87 
88  case 0xFE:
89  frame_set_by_callback = false;
90  break;
91 
92  default:
93  frame = callback & 0xFF;
94  break;
95  }
96 
97  /* If the lower 7 bits of the upper byte of the callback
98  * result are not empty, it is a sound effect. */
99  if (GB(callback, 8, 7) != 0 && _settings_client.sound.ambient) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
100  }
101  }
102 
103  if (!frame_set_by_callback) {
104  if (frame < num_frames) {
105  frame++;
106  } else if (frame == num_frames && spec->animation.status == ANIM_STATUS_LOOPING) {
107  /* This animation loops, so start again from the beginning */
108  frame = 0;
109  } else {
110  /* This animation doesn't loop, so stay here */
111  DeleteAnimatedTile(tile);
112  }
113  }
114 
115  bool changed = Tframehelper::Set(obj, tile, frame);
116  if (changed) MarkTileDirtyByTile(tile);
117  }
118 
131  static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, Textra extra_data = 0)
132  {
133  uint16_t callback = GetCallback(cb, random_bits, trigger, spec, obj, tile, extra_data);
134  if (callback == CALLBACK_FAILED) return;
135 
136  switch (callback & 0xFF) {
137  case 0xFD: /* Do nothing. */ break;
138  case 0xFE: AddAnimatedTile(tile, false); break;
139  case 0xFF: DeleteAnimatedTile(tile); break;
140  default:
141  bool changed = Tframehelper::Set(obj, tile, callback);
142  AddAnimatedTile(tile, changed);
143  break;
144  }
145 
146  /* If the lower 7 bits of the upper byte of the callback
147  * result are not empty, it is a sound effect. */
148  if (GB(callback, 8, 7) != 0 && _settings_client.sound.ambient) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
149  }
150 };
void DeleteAnimatedTile(TileIndex tile)
Removes the given tile from the animated tile table.
void AddAnimatedTile(TileIndex tile, bool mark_dirty)
Add the given tile to the animated tile table (if it does not exist yet).
Tile animation!
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static TickCounter counter
Monotonic counter, in ticks, since start of game.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
Definition: viewport.cpp:2057
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition: math_func.hpp:79
Definitions related to NewGRF animation.
static const uint8_t ANIM_STATUS_LOOPING
Animation is looping.
Callbacks that NewGRFs could implement.
CallbackID
List of implemented NewGRF callbacks.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
void PlayTileSound(const GRFFile *file, SoundID sound_id, TileIndex tile)
Play a NewGRF sound effect at the location of a specific tile.
Pseudo random number generator.
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
Helper class for a unified approach to NewGRF animation.
static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data=0)
Animate a single tile.
static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, Textra extra_data=0)
Check a callback to determine what the next animation step is and execute that step.
SoundSettings sound
sound effect settings
bool ambient
Play ambient, industry and town sounds.
Map writing/reading functions for tiles.
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition: tile_map.h:250
void SetAnimationFrame(Tile t, uint8_t frame)
Set a new animation frame.
Definition: tile_map.h:262
Definition of the tick-based game-timer.
Functions related to (drawing on) viewports.