crocparty/game/game_dialogue.c

158 lines
4.8 KiB
C

#include <assert.h>
#include <stdbool.h>
#include "device/device.h"
#include "sys/sys.h"
#include "game.h"
#define GAME_DIALOGUE_MAX_N_CHARS 256
typedef enum {
GAME_DIALOGUE_SCREEN_STATE_BOB_IN,
GAME_DIALOGUE_SCREEN_STATE_TYPEWRITER,
GAME_DIALOGUE_SCREEN_STATE_WAIT,
GAME_DIALOGUE_SCREEN_STATE_BOB_OUT,
} game_dialogue_screen_state;
struct {
bool visible;
bool modal;
const char* dialogue;
sys_i32 w;
sys_i32 h;
game_dialogue_screen_state state;
sys_i32 state_progress;
sys_i32 n_chars_shown;
sys_i32 n_chars;
} game_dialogue_screen;
uint8_t game_dialogue_frame;
void game_dialogue_display(const char* text, bool modal) {
game_dialogue_screen.visible = true;
game_dialogue_screen.modal = modal;
game_dialogue_screen.dialogue = text;
sys_measure_text(text, &game_dialogue_screen.w, &game_dialogue_screen.h);
game_dialogue_screen.state = GAME_DIALOGUE_SCREEN_STATE_BOB_IN;
game_dialogue_screen.state_progress = 0;
game_dialogue_screen.n_chars_shown = 0;
game_dialogue_screen.n_chars = strlen(text);
assert(game_dialogue_screen.n_chars <= GAME_DIALOGUE_MAX_N_CHARS);
}
void game_dialogue_update(bool* allow_input) {
game_dialogue_frame = ((uint32_t) game_dialogue_frame + 1) & 0xff;
if (
game_dialogue_screen.visible &&
game_dialogue_screen.state < GAME_DIALOGUE_SCREEN_STATE_BOB_OUT
) {
*allow_input = false;
if (sys_btnp(DEVICE_BUTTON_0, false)) {
if (game_dialogue_screen.state < GAME_DIALOGUE_SCREEN_STATE_WAIT) {
game_dialogue_screen.state = GAME_DIALOGUE_SCREEN_STATE_WAIT;
game_dialogue_screen.n_chars_shown = game_dialogue_screen.n_chars;
} else {
game_dialogue_screen.state = GAME_DIALOGUE_SCREEN_STATE_BOB_OUT;
game_dialogue_screen.state_progress = 0;
}
}
}
switch (game_dialogue_screen.state) {
case GAME_DIALOGUE_SCREEN_STATE_BOB_IN:
game_dialogue_screen.state_progress += 25;
if (game_dialogue_screen.state_progress >= 0x100) {
game_dialogue_screen.state = GAME_DIALOGUE_SCREEN_STATE_TYPEWRITER;
game_dialogue_screen.state_progress = 0;
}
break;
case GAME_DIALOGUE_SCREEN_STATE_TYPEWRITER:
if (game_dialogue_frame % 4 == 0) {
game_dialogue_screen.n_chars_shown = sys_min_i32(
game_dialogue_screen.n_chars_shown + 1,
game_dialogue_screen.n_chars
);
}
if (game_dialogue_screen.n_chars_shown >= game_dialogue_screen.n_chars) {
game_dialogue_screen.state = GAME_DIALOGUE_SCREEN_STATE_WAIT;
}
break;
case GAME_DIALOGUE_SCREEN_STATE_WAIT:
break;
case GAME_DIALOGUE_SCREEN_STATE_BOB_OUT:
game_dialogue_screen.state_progress += 25;
if (game_dialogue_screen.state_progress >= 0x100) {
game_dialogue_screen.visible = false;
game_dialogue_screen.state_progress = 0;
}
}
}
bool game_dialogue_is_busy() {
return game_dialogue_screen.visible;
}
void game_dialogue_draw() {
if (!game_dialogue_screen.visible) { return; }
sys_i32 x = DEVICE_W / 2 - game_dialogue_screen.w / 2;
sys_i32 y = DEVICE_H - game_dialogue_screen.h - 8;
if (game_dialogue_screen.modal) {
y = DEVICE_H / 2 - game_dialogue_screen.h / 2;
}
sys_i32 interp_amt = 0;
switch (game_dialogue_screen.state) {
case GAME_DIALOGUE_SCREEN_STATE_BOB_IN:
interp_amt = 255 - game_dialogue_screen.state_progress;
break;
case GAME_DIALOGUE_SCREEN_STATE_BOB_OUT:
interp_amt = game_dialogue_screen.state_progress;
break;
default:
break;
}
int x0 = x - 4;
int y0 = y - 4;
int x1 = x + game_dialogue_screen.w + 4;
int y1 = y + game_dialogue_screen.h + 4;
sys_i32 cx = (x0 + x1)/2;
sys_i32 cy = (y0 + y1)/2;
sys_oval_fill(
x0 + (cx - x0) * interp_amt / 256,
y0 + (cy - y0) * interp_amt / 256,
x1 + (cx - x1) * interp_amt / 256,
y1 + (cy - y1) * interp_amt / 256,
game_dialogue_screen.modal ? 0 : 1
);
if (game_dialogue_screen.state == GAME_DIALOGUE_SCREEN_STATE_BOB_OUT) {
return;
}
char chars[GAME_DIALOGUE_MAX_N_CHARS + 1];
strcpy(chars, game_dialogue_screen.dialogue);
chars[game_dialogue_screen.n_chars_shown] = 0;
if (game_dialogue_screen.modal) {
for (int dy = -2; dy <= 2; dy++) {
for (int dx = -2; dx <= 2; dx++) {
sys_print(chars, x+dx, y+dy, 8);
}
}
}
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
sys_print(chars, x+dx, y+dy, 0);
}
}
sys_print(chars, x, y, 7);
}