Animation player manager Pro (tm) 2k17

This commit is contained in:
laochailan 2017-10-03 17:25:38 +02:00
parent 73e2ae99df
commit c2b3747d95
No known key found for this signature in database
GPG key ID: 49BE98017AFBC943
15 changed files with 229 additions and 71 deletions

View file

@ -49,6 +49,7 @@ set(SRCs
global.c
events.c
player.c
aniplayer.c
projectile.c
progress.c
enemy.c

92
src/aniplayer.c Normal file
View file

@ -0,0 +1,92 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2017, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2017, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "aniplayer.h"
#include "list.h"
#include "global.h"
void aniplayer_create(AniPlayer *plr, Animation *ani) {
memset(plr,0,sizeof(AniPlayer));
plr->ani = ani;
}
void aniplayer_free(AniPlayer *plr) {
delete_all_elements((void **)&plr->queue,delete_element);
aniplayer_reset(plr);
}
void aniplayer_reset(AniPlayer *plr) { // resets to a neutral state with empty queue.
plr->stdrow = 0;
if(plr->queuesize > 0) { // abort the animation in the fastest fluent way.
delete_all_elements((void **)&plr->queue->next,delete_element);
plr->queuesize = 1;
plr->queue->delay = 0;
}
}
AniSequence *aniplayer_queue(AniPlayer *plr, int row, int loops, int delay) {
AniSequence *s = create_element_at_end((void **)plr->queue,sizeof(AniSequence));
plr->queuesize++;
s->row = row;
if(loops < 0)
log_fatal("Negative number of loops passed: %d",loops);
s->clocks = (loops+1)*plr->ani->cols*plr->ani->speed;
s->delay = delay;
return s;
}
void aniplayer_update(AniPlayer *plr) {
plr->clock++;
if(plr->queue) {
AniSequence *s = plr->queue;
if(s->clocks > 0) {
s->clocks--;
} else if(s->delay > 0) {
s->delay--;
} else {
delete_element((void **)&plr->queue,plr->queue);
plr->queuesize--;
plr->clock = 0;
}
}
}
void aniplayer_play(AniPlayer *plr, float x, float y) {
int col = (plr->clock/plr->ani->speed) % plr->ani->cols;
int row = plr->stdrow;
bool mirror = plr->mirrored;
if(plr->queue) {
AniSequence *s = plr->queue;
col = ((2*s->backwards-1)*s->clocks/plr->ani->speed) % plr->ani->cols;
row = s->row;
mirror = s->mirrored;
}
if(mirror) {
glPushMatrix();
glCullFace(GL_FRONT);
glTranslatef(x,y,0);
x = y = 0;
glScalef(-1,1,1);
}
draw_animation_p(x,y,col,row,plr->ani);
if(mirror) {
glCullFace(GL_BACK);
glPopMatrix();
}
}
void play_animation(Animation *ani, float x, float y, int row) { // the old way to draw animations without AniPlayer
draw_animation_p(x,y,(global.frames/ani->speed)%ani->cols,row,ani);
}

51
src/aniplayer.h Normal file
View file

@ -0,0 +1,51 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2017, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2017, Andrei Alexeyev <akari@alienslab.net>.
*/
#pragma once
/*
* This is a helper to play animations. It saves the state of the animation and
* allows queueing different animation sequences.
*/
#include "resource/animation.h"
typedef struct AniSequence AniSequence;
struct AniSequence{
struct AniSequence *next;
struct AniSequence *prev;
int row;
int clocks; // number of frames this sequence will be drawn
int delay; // after the sequence has played loops times before the next one is started.
bool mirrored;
bool backwards;
};
typedef struct {
Animation *ani;
int clock;
int stdrow; // this is the row rendered when the queue is empty
bool mirrored;
AniSequence *queue;
int queuesize;
} AniPlayer;
void aniplayer_create(AniPlayer *plr, Animation *ani);
void aniplayer_free(AniPlayer *plr);
void aniplayer_reset(AniPlayer *plr); // resets to a neutral state with empty queue.
AniSequence *aniplayer_queue(AniPlayer *plr, int row, int loops, int delay); // 0 loops: played one time
void aniplayer_update(AniPlayer *plr); // makes the inner clocks tick
void aniplayer_play(AniPlayer *plr, float x, float y);
void play_animation(Animation *ani, float x, float y, int row); // the old way to draw animations without AniPlayer

View file

@ -19,7 +19,7 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
strcpy(buf->name, name);
buf->pos = pos;
buf->ani = get_ani(ani);
aniplayer_create(&buf->ani, get_ani(ani));
if(dialog)
buf->dialog = get_tex(dialog);
@ -147,7 +147,7 @@ void draw_boss(Boss *boss) {
red = 0;
glColor4f(1,1-red,1-red/2,1);
draw_animation_p(creal(boss->pos), cimag(boss->pos) + 6*sin(global.frames/25.0), boss->anirow, boss->ani);
aniplayer_play(&boss->ani,creal(boss->pos), cimag(boss->pos) + 6*sin(global.frames/25.0));
glColor4f(1,1,1,1);
if(boss->current->type == AT_Move && global.frames - boss->current->starttime > 0 && boss_attack_is_final(boss, boss->current))
@ -376,7 +376,7 @@ void boss_finish_current_attack(Boss *boss) {
boss->current->finished = true;
boss->current->rule(boss, EVENT_DEATH);
boss->anirow = 0; // reset to standard animation
aniplayer_reset(&boss->ani);
if(t != AT_Move) {
stage_clear_hazards(true);
@ -420,6 +420,8 @@ void boss_finish_current_attack(Boss *boss) {
void process_boss(Boss **pboss) {
Boss *boss = *pboss;
aniplayer_update(&boss->ani);
if(!boss->current)
return;
@ -564,6 +566,7 @@ void free_boss(Boss *boss) {
for(int i = 0; i < boss->acount; i++)
free_attack(&boss->attacks[i]);
aniplayer_free(&boss->ani);
free(boss->name);
free(boss->attacks);
free(boss);

View file

@ -10,7 +10,7 @@
#include "util.h"
#include "difficulty.h"
#include "resource/animation.h"
#include "aniplayer.h"
#include "color.h"
enum {
@ -103,9 +103,8 @@ typedef struct Boss {
int acount;
Animation *ani;
AniPlayer ani;
Texture *dialog; // Used in spellcard intros
int anirow;
Color zoomcolor;

View file

@ -12,6 +12,7 @@
#include "global.h"
#include "projectile.h"
#include "list.h"
#include "aniplayer.h"
Enemy *create_enemy_p(Enemy **enemies, complex pos, int hp, EnemyDrawRule draw_rule, EnemyLogicRule logic_rule,
complex a1, complex a2, complex a3, complex a4) {
@ -162,7 +163,7 @@ void BigFairy(Enemy *e, int t) {
glCullFace(GL_FRONT);
glScalef(-1,1,1);
}
draw_animation(0, 0, e->moving, "bigfairy");
play_animation(get_ani("bigfairy"),0, 0, e->moving);
glPopMatrix();
if(e->dir)
@ -186,7 +187,7 @@ void Fairy(Enemy *e, int t) {
glCullFace(GL_FRONT);
glScalef(-1,1,1);
}
draw_animation(0, 0, e->moving, "fairy");
play_animation(get_ani("fairy"),0, 0, e->moving);
glPopMatrix();
glPopMatrix();

View file

@ -36,6 +36,21 @@ void *create_element(void **dest, size_t size) {
return e;
}
void* create_element_at_end(void **dest, size_t size) {
assert(dest != NULL);
if(*dest == NULL) {
return create_element((void **)dest,size);
}
List *end = NULL;
for(List *e = *dest; e; e = e->next) {
end = e;
}
return create_element((void **)&end,size);
}
void* create_element_at_priority(void **list_head, size_t size, int prio, int (*prio_func)(void*)) {
assert(list_head != NULL);
assert(size > 0);

View file

@ -15,6 +15,7 @@
#include <stdlib.h>
void* create_element(void **dest, size_t size);
void* create_element_at_end(void **dest, size_t size);
void* create_element_at_priority(void **list_head, size_t size, int prio, int (*prio_func)(void*));
void delete_element(void **dest, void *e);
void delete_all_elements(void **dest, void (callback)(void **, void *));

View file

@ -16,26 +16,6 @@
#include <SDL_bits.h>
void init_player(Player *plr) {
memset(plr, 0, sizeof(Player));
plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-64);
plr->lives = PLR_START_LIVES;
plr->bombs = PLR_START_BOMBS;
plr->deathtime = -1;
}
void prepare_player_for_next_stage(Player *plr) {
plr->recovery = 0;
plr->respawntime = 0;
plr->deathtime = -1;
plr->graze = 0;
plr->movetime = 0;
plr->prevmove = 0;
plr->prevmovetime = 0;
plr->axis_lr = 0;
plr->axis_ud = 0;
}
Animation *player_get_ani(Character cha) {
Animation *ani = NULL;
switch(cha) {
@ -50,6 +30,28 @@ Animation *player_get_ani(Character cha) {
return ani;
}
void init_player(Player *plr) {
memset(plr, 0, sizeof(Player));
plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-64);
plr->lives = PLR_START_LIVES;
plr->bombs = PLR_START_BOMBS;
plr->deathtime = -1;
aniplayer_create(&plr->ani, player_get_ani(plr->cha));
}
void prepare_player_for_next_stage(Player *plr) {
plr->recovery = 0;
plr->respawntime = 0;
plr->deathtime = -1;
plr->graze = 0;
plr->movetime = 0;
plr->prevmove = 0;
plr->prevmovetime = 0;
plr->axis_lr = 0;
plr->axis_ud = 0;
}
static void player_full_power(Player *plr) {
play_sound("full_power");
stage_clear_hazards(false);
@ -99,6 +101,8 @@ void player_move(Player *plr, complex delta) {
}
void player_draw(Player* plr) {
plr->ani.ani = player_get_ani(plr->cha); // because plr->cha is not set at init
// FIXME: death animation?
if(plr->deathtime > global.frames)
return;
@ -118,11 +122,6 @@ void player_draw(Player* plr) {
glPopMatrix();
}
glDisable(GL_CULL_FACE);
if(plr->dir) {
glPushMatrix();
glScalef(-1,1,1);
}
int clr_changed = 0;
@ -131,16 +130,11 @@ void player_draw(Player* plr) {
clr_changed = 1;
}
draw_animation_p(0, 0, !plr->moving, player_get_ani(plr->cha));
aniplayer_play(&plr->ani,0,0);
if(clr_changed)
glColor3f(1,1,1);
if(plr->dir)
glPopMatrix();
glEnable(GL_CULL_FACE);
if(plr->focus) {
glPushMatrix();
glColor4f(1, 1, 1, plr->focus / 30.0);
@ -173,6 +167,7 @@ static void player_fail_spell(Player *plr) {
void player_logic(Player* plr) {
process_enemies(&plr->slaves);
aniplayer_update(&plr->ani);
if(plr->deathtime < -1) {
plr->deathtime++;
plr->pos -= I;
@ -473,12 +468,17 @@ bool player_applymovement_gamepad(Player *plr) {
return true;
}
static void player_ani_moving(Player *plr, bool moving, bool dir) {
plr->ani.stdrow = !moving;
plr->ani.mirrored = dir;
}
void player_applymovement(Player *plr) {
if(plr->deathtime < -1)
return;
bool gamepad = player_applymovement_gamepad(plr);
plr->moving = false;
player_ani_moving(plr,false,false);
int up = plr->inputflags & INFLAG_UP,
down = plr->inputflags & INFLAG_DOWN,
@ -486,11 +486,9 @@ void player_applymovement(Player *plr) {
right = plr->inputflags & INFLAG_RIGHT;
if(left && !right) {
plr->moving = true;
plr->dir = 1;
player_ani_moving(plr,true,true);
} else if(right && !left) {
plr->moving = true;
plr->dir = 0;
player_ani_moving(plr,true,false);
}
if(gamepad)

View file

@ -15,6 +15,7 @@
#include "util.h"
#include "enemy.h"
#include "gamepad.h"
#include "aniplayer.h"
#include "resource/animation.h"
enum {
@ -70,9 +71,7 @@ typedef enum {
typedef struct {
complex pos;
short focus;
bool moving;
short dir;
short power;
int graze;
@ -89,6 +88,8 @@ typedef struct {
int deathtime;
int respawntime;
AniPlayer ani;
Character cha;
ShotMode shot;
Enemy *slaves;

View file

@ -368,8 +368,6 @@ int youmu_split(Enemy *e, int t) {
FROM_TO(0, 220, 1) {
float talt = atan((t-e->args[0]/2)/30.0)*10+atan(-e->args[0]/2);
global.plr.pos = VIEWPORT_W/2.0 + (VIEWPORT_H-80)*I + VIEWPORT_W/3.0*sin(talt);
global.plr.moving = true;
global.plr.dir = 1.0/(pow(e->args[0]/2-t,2) + 1)*cos(talt) < 0;
}
return 1;

View file

@ -8,7 +8,6 @@
#include "animation.h"
#include "texture.h"
#include "global.h"
#include "resource.h"
#include "list.h"
@ -93,11 +92,11 @@ Animation *get_ani(const char *name) {
return get_resource(RES_ANIM, name, RESF_DEFAULT)->animation;
}
void draw_animation(float x, float y, int row, const char *name) {
draw_animation_p(x, y, row, get_ani(name));
void draw_animation(float x, float y, int col, int row, const char *name) {
draw_animation_p(x, y, col, row, get_ani(name));
}
void draw_animation_p(float x, float y, int row, Animation *ani) {
void draw_animation_p(float x, float y, int col, int row, Animation *ani) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ani->tex->gltex);
@ -114,7 +113,6 @@ void draw_animation_p(float x, float y, int row, Animation *ani) {
glPushMatrix();
glScalef(s,t,1);
int col = global.frames/ani->speed % ani->cols;
if(col || row)
glTranslatef(col, row, 0);
glMatrixMode(GL_MODELVIEW);

View file

@ -17,7 +17,7 @@ typedef struct Animation {
int cols;
int w;
int h;
int h;
int speed;
@ -31,8 +31,8 @@ void* load_animation_end(void *opaque, const char *filename, unsigned int flags)
Animation *get_ani(const char *name);
void draw_animation(float x, float y, int row, const char *name);
void draw_animation_p(float x, float y, int row, Animation *ani);
void draw_animation(float x, float y, int col, int row, const char *name);
void draw_animation_p(float x, float y, int col, int row, Animation *ani);
#define ANI_PATH_PREFIX TEX_PATH_PREFIX
#define ANI_EXTENSION ".ani"

View file

@ -164,9 +164,9 @@ void cirno_perfect_freeze(Boss *c, int time) {
int d = max(0, global.diff - D_Normal);
AT(160-50*d)
c->anirow = 1;
c->ani.stdrow = 1;
AT(220+30*d)
c->anirow = 0;
c->ani.stdrow = 0;
FROM_TO_SND("shot1_loop", 160 - 50*d, 220 + 30*d, 6-global.diff/2) {
float r1, r2;
@ -235,9 +235,9 @@ void cirno_iceplosion0(Boss *c, int time) {
return;
AT(20)
c->anirow = 1;
c->ani.stdrow = 1;
AT(30)
c->anirow = 0;
c->ani.stdrow = 0;
AT(20) {
play_sound("shot_special1");
}
@ -285,9 +285,9 @@ void cirno_crystal_rain(Boss *c, int time) {
}
AT(100)
c->anirow = 1;
c->ani.stdrow = 1;
AT(400)
c->anirow = 0;
c->ani.stdrow = 0;
FROM_TO(100, 400, 120-20*global.diff - 10 * hdiff) {
float i;
bool odd = (hdiff? (_i&1) : 0);
@ -318,9 +318,9 @@ void cirno_iceplosion1(Boss *c, int time) {
play_sound("shot_special1");
}
AT(20)
c->anirow = 1;
c->ani.stdrow = 1;
AT(30)
c->anirow = 0;
c->ani.stdrow = 0;
FROM_TO(20,30,2) {
int i;
@ -468,7 +468,7 @@ void cirno_snow_halation(Boss *c, int time) {
AT(60) {
center = global.plr.pos;
rotation = (M_PI/2.0) * (1 + time / 300);
c->anirow = 1;
c->ani.stdrow = 1;
}
const int interval = 3;
@ -487,7 +487,7 @@ void cirno_snow_halation(Boss *c, int time) {
}
AT(80 + interval * projs/2) {
c->anirow = 0;
c->ani.stdrow = 0;
}
}
@ -522,9 +522,9 @@ void cirno_icicle_fall(Boss *c, int time) {
GO_TO(c, VIEWPORT_W/2.0+120.0*I, 0.01);
AT(20)
c->anirow = 1;
c->ani.stdrow = 1;
AT(200)
c->anirow = 0;
c->ani.stdrow = 0;
FROM_TO(20,200,30-3*global.diff) {
play_sound("shot1");
for(float i = 2-0.2*global.diff; i < 5; i+=1./(1+global.diff)) {
@ -585,9 +585,9 @@ void cirno_crystal_blizzard(Boss *c, int time) {
}
AT(330)
c->anirow = 1;
c->ani.stdrow = 1;
AT(700)
c->anirow = 0;
c->ani.stdrow = 0;
FROM_TO_SND("shot1_loop",330, 700, 1) {
GO_TO(c, global.plr.pos, 0.01);

View file

@ -651,7 +651,7 @@ void hina_spell_bg(Boss *h, int time) {
draw_texture(0, 0, "stage2/spellbg2");
glPopMatrix();
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
draw_animation(creal(h->pos), cimag(h->pos), 0, "fire");
play_animation(get_ani("fire"),creal(h->pos), cimag(h->pos), 0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);