finale animation
This commit is contained in:
parent
0a7e4fbcab
commit
73dc8df3f3
13
src/duet.h
13
src/duet.h
|
@ -109,6 +109,8 @@ typedef struct game {
|
|||
float time_transition;
|
||||
/* Time spent during episode transition */
|
||||
float time_episode_transition;
|
||||
/* Time sent during finale */
|
||||
float time_finale;
|
||||
|
||||
/* Forced rotation speed for level transitions and death rewind */
|
||||
float forced_player_rota;
|
||||
|
@ -118,14 +120,19 @@ typedef struct game {
|
|||
// Rendering
|
||||
//---
|
||||
|
||||
void dcircle(int x, int y, int r, int color, bool fill);
|
||||
void dcircle(int x, int y, int r, int color, bool fill, int rep1, int rep2);
|
||||
|
||||
void dtriangle(int x1, int y1, int x2, int y2, int x3, int y3, int color,
|
||||
int opacity);
|
||||
|
||||
void drectoid(rect_t const *r, float extra_size, int color);
|
||||
|
||||
void render_player(int x, int y, float angle);
|
||||
void render_player(int x, int y, float angle, int r, float opacity);
|
||||
|
||||
void render_glow(int x, int y, int r1, int r2, int c1, int c2, float angle,
|
||||
float opacity);
|
||||
|
||||
void radial_fadeout(int x, int y, int r1, int r2, int c);
|
||||
|
||||
//---
|
||||
// Duet Text
|
||||
|
@ -140,7 +147,7 @@ void duet_text_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
//---
|
||||
|
||||
void player_position(float angle,
|
||||
float *x1, float *y1, float *x2, float *y2);
|
||||
float *x1, float *y1, float *x2, float *y2, int r);
|
||||
|
||||
bool player_collision(game_t const *game);
|
||||
|
||||
|
|
66
src/main.c
66
src/main.c
|
@ -209,6 +209,15 @@ void game_loop(int current_episode, int current_level)
|
|||
float dt = FRAME_DURATION * time_direction;
|
||||
game.time += dt;
|
||||
|
||||
/* Finale */
|
||||
if(state == State_Finale) {
|
||||
game.time_finale += dt;
|
||||
if(game.time_finale >= 0.5) {
|
||||
float t = game.time_finale - 0.5;
|
||||
game.player_rota -= 1.57 * t / 3 * dt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Input analysis */
|
||||
|
||||
bool rotate_left=false, rotate_right=false;
|
||||
|
@ -228,6 +237,10 @@ void game_loop(int current_episode, int current_level)
|
|||
if (keydown(KEY_0)) {
|
||||
rotate_right = true;
|
||||
}
|
||||
if ((keydown(KEY_0) || keydown(KEY_7)) && state == State_Finale &&
|
||||
game.time_finale >= 16.0) {
|
||||
stop_playing = true;
|
||||
}
|
||||
if (stop_playing)
|
||||
break;
|
||||
|
||||
|
@ -244,8 +257,10 @@ void game_loop(int current_episode, int current_level)
|
|||
current_level = 0;
|
||||
changed_episode = true;
|
||||
}
|
||||
if(current_episode >= episode_count)
|
||||
break;
|
||||
if(current_episode >= episode_count) {
|
||||
state = State_Finale;
|
||||
continue;
|
||||
}
|
||||
|
||||
load_level(&game, episodes[current_episode].levels[current_level]);
|
||||
|
||||
|
@ -305,12 +320,16 @@ void game_loop(int current_episode, int current_level)
|
|||
dclear(C_BLACK);
|
||||
|
||||
// Cool animated background
|
||||
int bg_color = 4;
|
||||
if(state == State_Finale)
|
||||
bg_color = (game.time_finale >= 2) ? 0 : 4 - 2 * game.time_finale;
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
int x=DWIDTH+2-40*i-20, y=20;
|
||||
|
||||
rect_t rect = { .x=x, .y=y, .w=20, .h=20, .r=1.0*game.time,
|
||||
.opacity=256 };
|
||||
drectoid(&rect, 0, C_RGB(4, 4, 4));
|
||||
drectoid(&rect, 0, C_RGB(bg_color, bg_color, bg_color));
|
||||
}
|
||||
for(int j = 1; j < 6; j++) {
|
||||
int lines = (j == 5) ? 24 : 40;
|
||||
|
@ -336,7 +355,46 @@ void game_loop(int current_episode, int current_level)
|
|||
DTEXT_MIDDLE, episodes[current_episode].name, -1);
|
||||
}
|
||||
|
||||
render_player(PLAYER_X, DHEIGHT/2, game.player_rota);
|
||||
if(state == State_Finale && game.time_finale >= 11.0) {
|
||||
float t = game.time_finale - 11.0;
|
||||
float rt = fminf(1.0, t / 4.0);
|
||||
float opacity = fminf(1.0, t / 2.0);
|
||||
render_glow(DWIDTH/2, DHEIGHT/2, 24*rt, 20*rt, C_RGB(15,12,13),
|
||||
C_RGB(10,5,8), -t / 2.0, opacity);
|
||||
}
|
||||
|
||||
float opacity = 1.0;
|
||||
int x = PLAYER_X;
|
||||
int r = PLAYER_R;
|
||||
|
||||
if(state == State_Finale) {
|
||||
opacity = fmaxf(1 - game.time_finale / 2, 0);
|
||||
/* Interpolate between x1 and x2 in 10.0 seconds along (1-x)² */
|
||||
int x1 = PLAYER_X, x2 = DWIDTH / 2;
|
||||
float t = fminf(game.time_finale / 10.0, 1.0);
|
||||
t = 1 - (1-t)*(1-t);
|
||||
x = (1 - t) * x1 + t * x2;
|
||||
/* Interpolate radius between PLAYER_R and 0 in 10.0 seconds */
|
||||
t = fmaxf(0.0, fminf(1.0, (game.time_finale - 2.0) / 10.0));
|
||||
r = PLAYER_R * (1 - t);
|
||||
}
|
||||
render_player(x, DHEIGHT/2, game.player_rota, r, opacity);
|
||||
|
||||
/* Radial fade out */
|
||||
if(state == State_Finale && game.time_finale >= 12.0) {
|
||||
float t = fminf(1.0, (game.time_finale - 11.0) / 4.0);
|
||||
int r1 = PLAYER_SIZE + (12 - PLAYER_SIZE) * t;
|
||||
int r2 = PLAYER_SIZE + (24 - PLAYER_SIZE) * t;
|
||||
radial_fadeout(DWIDTH/2, DHEIGHT/2, r1, r2, C_BLACK);
|
||||
}
|
||||
/* Final text */
|
||||
if(state == State_Finale && game.time_finale >= 14.0) {
|
||||
float t = fminf(1.0, (game.time_finale - 14.0) / 2.0);
|
||||
int c = 31*t;
|
||||
c = C_RGB(c, c, c);
|
||||
duet_text_opt(DWIDTH/2 - 30, DHEIGHT/2, c, C_NONE, DTEXT_CENTER,
|
||||
DTEXT_MIDDLE, "I am here", -1);
|
||||
}
|
||||
|
||||
float extra_size = 1 + sinf(6 * game.time);
|
||||
for(int i = 0; i < game.rect_count; i++)
|
||||
|
|
|
@ -102,7 +102,7 @@ int main_menu(int *episode, int *level)
|
|||
dclear(C_BLACK);
|
||||
dimage(330+scroll, DHEIGHT/2 - img_title.height / 2,
|
||||
&img_title);
|
||||
render_player(268+scroll, DHEIGHT/2, time * 0.8);
|
||||
render_player(268+scroll, DHEIGHT/2, time * 0.8, PLAYER_R, 1.0);
|
||||
|
||||
int x=188+scroll, y=40;
|
||||
for(int i = 0; i < episode_count; i++) {
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
#include "duet.h"
|
||||
|
||||
void player_position(float angle,
|
||||
float *x1, float *y1, float *x2, float *y2)
|
||||
float *x1, float *y1, float *x2, float *y2, int r)
|
||||
{
|
||||
int x=PLAYER_X, y=DHEIGHT/2;
|
||||
|
||||
float sin, cos;
|
||||
sincosf(angle, &sin, &cos);
|
||||
|
||||
if(x1) *x1 = x + sin * PLAYER_R;
|
||||
if(y1) *y1 = y + cos * PLAYER_R;
|
||||
if(x1) *x1 = x + sin * r;
|
||||
if(y1) *y1 = y + cos * r;
|
||||
|
||||
if(x2) *x2 = x - sin * PLAYER_R;
|
||||
if(y2) *y2 = y - cos * PLAYER_R;
|
||||
if(x2) *x2 = x - sin * r;
|
||||
if(y2) *y2 = y - cos * r;
|
||||
}
|
||||
|
||||
bool player_collision(game_t const *game)
|
||||
{
|
||||
float x1, y1, x2, y2;
|
||||
player_position(game->player_rota, &x1, &y1, &x2, &y2);
|
||||
player_position(game->player_rota, &x1, &y1, &x2, &y2, PLAYER_R);
|
||||
|
||||
for(int i = 0; i < game->rect_count; i++) {
|
||||
if(rect_circle_collide(&game->rects[i], x1, y1, PLAYER_SIZE))
|
||||
|
|
87
src/render.c
87
src/render.c
|
@ -1,20 +1,34 @@
|
|||
#include "duet.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Extreme hacks for intersecting circles; we draw a pixel as [color], but if
|
||||
there is already [rep1] on that pixel we turn it into [rep2], and if we see
|
||||
[rep2] we don't paint over it */
|
||||
void dcircle_pixel(int x, int y, int color, int rep1, int rep2)
|
||||
{
|
||||
if(x < 0 || x >= DWIDTH || y < 0 || y >= DHEIGHT)
|
||||
return;
|
||||
int i = DWIDTH * y + x;
|
||||
if(gint_vram[i] == rep2)
|
||||
return;
|
||||
gint_vram[i] = (gint_vram[i] == rep1) ? rep2 : color;
|
||||
}
|
||||
/* Bresenham algorithm */
|
||||
void dcircle(int cx, int cy, int r0, int color, bool fill)
|
||||
void dcircle(int cx, int cy, int r0, int color, bool fill, int rep1, int rep2)
|
||||
{
|
||||
int x=-r0, y=0, e=2-2*r0, r=r0;
|
||||
|
||||
do {
|
||||
dpixel(cx-x, cy-y, color);
|
||||
dpixel(cx+y, cy-x, color);
|
||||
dpixel(cx+x, cy+y, color);
|
||||
dpixel(cx-y, cy+x, color);
|
||||
dcircle_pixel(cx-x, cy-y, color, rep1, rep2);
|
||||
dcircle_pixel(cx+y, cy-x, color, rep1, rep2);
|
||||
dcircle_pixel(cx+x, cy+y, color, rep1, rep2);
|
||||
dcircle_pixel(cx-y, cy+x, color, rep1, rep2);
|
||||
|
||||
if(fill) {
|
||||
dline(cx+x, cy-y, cx-x, cy-y, color);
|
||||
dline(cx+x, cy+y, cx-x, cy+y, color);
|
||||
for(int sx = cx+x; sx <= cx-x; sx++) {
|
||||
dcircle_pixel(sx, cy-y, color, rep1, rep2);
|
||||
dcircle_pixel(sx, cy+y, color, rep1, rep2);
|
||||
}
|
||||
}
|
||||
|
||||
r = e;
|
||||
|
@ -147,18 +161,67 @@ void drectoid(rect_t const *r, float extra_size, int color)
|
|||
dtriangle(x[2], y[2], x[3], y[3], x[0], y[0], color, r->opacity);
|
||||
}
|
||||
|
||||
void render_player(int x, int y, float angle)
|
||||
void render_player(int x, int y, float angle, int r, float opacity)
|
||||
{
|
||||
dcircle(x, y, PLAYER_R, C_RGB(10, 10, 10), false);
|
||||
int c = 10 * opacity;
|
||||
dcircle(x, y, r, C_RGB(c, c, c), false, -1, -1);
|
||||
|
||||
float x1, y1, x2, y2;
|
||||
player_position(angle, &x1, &y1, &x2, &y2);
|
||||
player_position(angle, &x1, &y1, &x2, &y2, r);
|
||||
|
||||
x1 = x1 - PLAYER_X + x;
|
||||
y1 = y1 - DHEIGHT/2 + y;
|
||||
x2 = x2 - PLAYER_X + x;
|
||||
y2 = y2 - DHEIGHT/2 + y;
|
||||
|
||||
dcircle(x1, y1, PLAYER_SIZE, C_RGB(0, 16, 26), true);
|
||||
dcircle(x2, y2, PLAYER_SIZE, C_RGB(31, 6, 0), true);
|
||||
int c1 = C_RGB(0, 16, 26);
|
||||
int c2 = C_RGB(31, 6, 0);
|
||||
int c3 = C_WHITE;
|
||||
|
||||
dcircle(x1, y1, PLAYER_SIZE, c1, true, -1, -1);
|
||||
dcircle(x2, y2, PLAYER_SIZE, c2, true, c1, c3);
|
||||
}
|
||||
|
||||
void render_glow(int x, int y, int r1, int r2, int color1, int color2,
|
||||
float angle, float opacity)
|
||||
{
|
||||
float s1, c1, s2, c2;
|
||||
sincosf(angle, &s2, &c2);
|
||||
|
||||
for(int i = 0; i < 16; i++) {
|
||||
s1 = s2;
|
||||
c1 = c2;
|
||||
angle += M_PI / 8.0;
|
||||
sincosf(angle, &s2, &c2);
|
||||
|
||||
int r = (i & 1) ? r2 : r1;
|
||||
int color = (i & 1) ? color2 : color1;
|
||||
dtriangle(x, y, x+r*c2, y+r*s2, x+r*c1, y+r*s1, color, 256 * opacity);
|
||||
}
|
||||
}
|
||||
|
||||
void radial_fadeout(int cx, int cy, int r1, int r2, int c)
|
||||
{
|
||||
int x1 = max(0, cx - r2);
|
||||
int x2 = min(DWIDTH-1, cx + r2);
|
||||
int y1 = max(0, cy - r2);
|
||||
int y2 = min(DHEIGHT-1, cy + r2);
|
||||
|
||||
for(int y = y1; y <= y2; y++)
|
||||
for(int x = x1; x <= x2; x++) {
|
||||
int i = DWIDTH * y + x;
|
||||
int r_sq = (x - cx) * (x - cx) + (y - cy) * (y - cy);
|
||||
|
||||
if(r_sq < r1 * r1)
|
||||
continue;
|
||||
if(r_sq > r2 * r2) {
|
||||
gint_vram[i] = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
float r = sqrtf(r_sq);
|
||||
float opacity = fmaxf(0.0, fminf(1.0, (r - r1) / (r2 - r1)));
|
||||
|
||||
gint_vram[i] = blend(gint_vram[i], c, 256*opacity);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue