# newgrf_animation_base.h

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 /* No inclusion guards as this file must only be included from .cpp files. */
    00013
    00014 #include "animated_tile_func.h"
    00015 #include "core/random_func.hpp"
    00016 #include "date_func.h"
    00017 #include "viewport_func.h"
    00018 #include "newgrf_animation_type.h"
    00019 #include "newgrf_callbacks.h"
    00020 #include "tile_map.h"
    00021
    00030 template <typename Tbase, typename Tspec, typename Tobj, typename Textra, uint16 (*GetCallback)(CallbackID callback, uint32 param1, uint32 param2, const Tspec *statspec, Tobj *st, TileIndex tile, Textra extra_data)>
    00031 struct AnimationBase {
    00041   static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data = 0)
    00042   {
    00043     assert(spec != NULL);
    00044
    00045     /* Acquire the animation speed from the NewGRF. */
    00046     uint8 animation_speed = spec->animation.speed;
    00047     if (HasBit(spec->callback_mask, Tbase::cbm_animation_speed)) {
    00048       uint16 callback = GetCallback(Tbase::cb_animation_speed, 0, 0, spec, obj, tile, extra_data);
    00049       if (callback != CALLBACK_FAILED) {
    00050         if (callback >= 0x100 && spec->grf_prop.grffile->grf_version >= 8) ErrorUnknownCallbackResult(spec->grf_prop.grffile->grfid, Tbase::cb_animation_speed, callback);
    00051         animation_speed = Clamp(callback & 0xFF, 0, 16);
    00052       }
    00053     }
    00054
    00055     /* An animation speed of 2 means the animation frame changes 4 ticks, and
    00056      * increasing this value by one doubles the wait. 0 is the minimum value
    00057      * allowed for animation_speed, which corresponds to 30ms, and 16 is the
    00058      * maximum, corresponding to around 33 minutes. */
    00059     if (_tick_counter % (1 << animation_speed) != 0) return;
    00060
    00061     uint8 frame      = GetAnimationFrame(tile);
    00062     uint8 num_frames = spec->animation.frames;
    00063
    00064     bool frame_set_by_callback = false;
    00065
    00066     if (HasBit(spec->callback_mask, Tbase::cbm_animation_next_frame)) {
    00067       uint16 callback = GetCallback(Tbase::cb_animation_next_frame, random_animation ? Random() : 0, 0, spec, obj, tile, extra_data);
    00068
    00069       if (callback != CALLBACK_FAILED) {
    00070         frame_set_by_callback = true;
    00071
    00072         switch (callback & 0xFF) {
    00073           case 0xFF:
    00074             DeleteAnimatedTile(tile);
    00075             break;
    00076
    00077           case 0xFE:
    00078             frame_set_by_callback = false;
    00079             break;
    00080
    00081           default:
    00082             frame = callback & 0xFF;
    00083             break;
    00084         }
    00085
    00086         /* If the lower 7 bits of the upper byte of the callback
    00087          * result are not empty, it is a sound effect. */
    00088         if (GB(callback, 8, 7) != 0 && _settings_client.sound.ambient) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
    00089       }
    00090     }
    00091
    00092     if (!frame_set_by_callback) {
    00093       if (frame < num_frames) {
    00094         frame++;
    00095       } else if (frame == num_frames && spec->animation.status == ANIM_STATUS_LOOPING) {
    00096         /* This animation loops, so start again from the beginning */
    00097         frame = 0;
    00098       } else {
    00099         /* This animation doesn't loop, so stay here */
    00100         DeleteAnimatedTile(tile);
    00101       }
    00102     }
    00103
    00104     SetAnimationFrame(tile, frame);
    00105     MarkTileDirtyByTile(tile);
    00106   }
    00107
    00120   static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32 random_bits, uint32 trigger, Textra extra_data = 0)
    00121   {
    00122     uint16 callback = GetCallback(cb, random_bits, trigger, spec, obj, tile, extra_data);
    00123     if (callback == CALLBACK_FAILED) return;
    00124
    00125     switch (callback & 0xFF) {
    00126       case 0xFD: /* Do nothing. */         break;
    00127       case 0xFE: AddAnimatedTile(tile);    break;
    00128       case 0xFF: DeleteAnimatedTile(tile); break;
    00129       default:
    00130         SetAnimationFrame(tile, callback);
    00131         AddAnimatedTile(tile);
    00132         break;
    00133     }
    00134
    00135     /* If the lower 7 bits of the upper byte of the callback
    00136      * result are not empty, it is a sound effect. */
    00137     if (GB(callback, 8, 7) != 0 && _settings_client.sound.ambient) PlayTileSound(spec->grf_prop.grffile, GB(callback, 8, 7), tile);
    00138   }
    00139 };