Update: Translations from eints
[openttd-github.git] / src / newgrf_animation_base.h
blobab919e2e84317b434101f7e9261a48c2761d1fc8
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 */
8 /** @file newgrf_animation_base.h Function implementations related to NewGRF animation. */
10 /* No inclusion guards as this file must only be included from .cpp files. */
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"
20 template <typename Tobj>
21 struct TileAnimationFrameAnimationHelper {
22 static uint8_t Get(Tobj *, TileIndex tile) { return GetAnimationFrame(tile); }
23 static bool Set(Tobj *, TileIndex tile, uint8_t frame)
25 uint8_t prev_frame = GetAnimationFrame(tile);
26 if (prev_frame == frame) return false;
28 SetAnimationFrame(tile, frame);
29 return true;
33 /**
34 * Helper class for a unified approach to NewGRF animation.
35 * @tparam Tbase Instantiation of this class.
36 * @tparam Tspec NewGRF specification related to the animated tile.
37 * @tparam Tobj Object related to the animated tile.
38 * @tparam Textra Custom extra callback data.
39 * @tparam GetCallback The callback function pointer.
40 * @tparam Tframehelper The animation frame get/set helper.
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 {
44 /**
45 * Animate a single tile.
46 * @param spec Specification related to the tile.
47 * @param obj Object related to the tile.
48 * @param tile Tile to animate changes for.
49 * @param random_animation Whether to pass random bits to the "next frame" callback.
50 * @param extra_data Custom extra callback data.
52 static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data = 0)
54 assert(spec != nullptr);
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);
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;
72 uint8_t frame = Tframehelper::Get(obj, tile);
73 uint8_t num_frames = spec->animation.frames;
75 bool frame_set_by_callback = false;
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);
80 if (callback != CALLBACK_FAILED) {
81 frame_set_by_callback = true;
83 switch (callback & 0xFF) {
84 case 0xFF:
85 DeleteAnimatedTile(tile);
86 break;
88 case 0xFE:
89 frame_set_by_callback = false;
90 break;
92 default:
93 frame = callback & 0xFF;
94 break;
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);
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);
115 bool changed = Tframehelper::Set(obj, tile, frame);
116 if (changed) MarkTileDirtyByTile(tile);
120 * Check a callback to determine what the next animation step is and
121 * execute that step. This includes stopping and starting animations
122 * as well as updating animation frames and playing sounds.
123 * @param cb The callback to actually call.
124 * @param spec Specification related to the tile.
125 * @param obj Object related to the tile.
126 * @param tile Tile to consider animation changes for.
127 * @param random_bits Random bits for this update. To be passed as parameter to the NewGRF.
128 * @param trigger What triggered this update? To be passed as parameter to the NewGRF.
129 * @param extra_data Custom extra data for callback processing.
131 static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, Textra extra_data = 0)
133 uint16_t callback = GetCallback(cb, random_bits, trigger, spec, obj, tile, extra_data);
134 if (callback == CALLBACK_FAILED) return;
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;
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);