finale animation

This commit is contained in:
Lephenixnoir 2022-01-24 00:39:12 +01:00
parent 0a7e4fbcab
commit 73dc8df3f3
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
5 changed files with 154 additions and 26 deletions

View File

@ -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);

View File

@ -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++)

View File

@ -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++) {

View File

@ -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))

View File

@ -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);
}
}