taisei/src/aniplayer.h
2024-05-17 14:11:48 +02:00

106 lines
4.2 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
// In principle, playing an animation does not require any state, just a call to
// get_animation_frame will get you the right sprite out of a sequence. But if
// you want easy queueing and seamless switching between animation sequences,
// you can use an AniPlayer.
//
// It works like this: AniPlayer contains a queue where you can enqueue
// animation sequences using aniplayer_queue. Then the player will count as many
// loops/frames as you specified and once done, go to the next queue entry. If
// there is no other queue entry, the current entry is looped until something is
// added. Then the queue will switch at the next sequence endpoint for seamlessness.
//
// For aniplayer_queue takes a number of minimum loops that should be played
// before the animation is switched (remember it goes on forever if there is
// nothing to follow). For convenience there is also aniplayer_queue_frames
// which allows you to specify a minimum number of frames.
//
// If you need to go to a new sequence asap but still in a seamless way you can use
// aniplayer_soft_switch to forget about the queue and do your sequence directly
// after the currently running one.
//
// If that is still not okay for you (You just need to switch instantly, I
// respect that) you can call aniplayer_hard_switch to switch to a new sequence
// directly. This wont be seamless though. Seamlessness is the biggest quirk of
// this api because it typically delays things and you need to queue early
// enough to get the queue transition at the right time.
//
// Examples:
//
// 1. make Hina spin one loop
//
// aniplayer_queue(&h->ani, "guruguru", 1);
// aniplayer_queue(&h->ani, "main", 0);
//
// The second line is important. Giving 0 minloops as an argument is common.
// It just means that the animation should not play longer than need be.
// In fact, having a call with minloops != 0 not followed by another call does
// not make much sense. If you want to play n-loops you need to provide
// something to switch to.
//
// 2. make Cirno flex for 30 frames
//
// aniplayer_queue_frames(&c->ani, "(9)", 30);
// aniplayer_queue(&c->ani, "main", 0);
//
// Same story. This flavor is also useful in some spells.
//
// 3. make Cirno flex for the whole spell
//
// aniplayer_queue(&c->ani, "(9)", 0);
//
// 4. make the player react to an input instantly
//
// aniplayer_hard_switch(&plr->ani, "right", 0);
//
// Similar examples occur throughout the code so if you want context, you can just look there.
//
#include "resource/animation.h"
#include "list.h"
typedef struct AniQueueEntry AniQueueEntry;
struct AniQueueEntry{
LIST_INTERFACE(AniQueueEntry);
AniSequence *sequence;
int clock; // frame counter. As long as clock < duration this entry will keep running
int duration; // number of frames this sequence will be drawn
};
typedef struct AniPlayer AniPlayer;
struct AniPlayer{
Animation *ani;
LIST_ANCHOR(AniQueueEntry) queue;
int queuesize;
};
void aniplayer_create(AniPlayer *plr, Animation *ani, const char *startsequence) attr_nonnull(1, 2);
void aniplayer_free(AniPlayer *plr);
// AniPlayer version of animation_get_frame.
Sprite *aniplayer_get_frame(AniPlayer *plr) attr_nonnull(1);
// See comment above for these three stooges.
AniQueueEntry *aniplayer_queue(AniPlayer *plr, const char *seqname, int minloops) attr_nonnull(1);
AniQueueEntry *aniplayer_soft_switch(AniPlayer *plr, const char *seqname, int minloops) attr_nonnull(1);
AniQueueEntry *aniplayer_hard_switch(AniPlayer *plr, const char *seqname, int minloops) attr_nonnull(1);
// This fourth stooge also queues a sequence but for at least the given amount of frames
// Useful in the stage code.
AniQueueEntry *aniplayer_queue_frames(AniPlayer *plr, const char *seqname, int minframes) attr_nonnull(1);
// AniPlayers need to be actively updated in order to tick (unlike most of
// the rest of the game which just uses global.frames as a counter). So you
// need to call this function once per frame to make an animation move.
void aniplayer_update(AniPlayer *plr) attr_nonnull(1);