2024-02-29 04:48:22 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "device/device.h"
|
2024-02-29 22:31:44 +00:00
|
|
|
#include "sys/sys.h"
|
2024-02-29 04:48:22 +00:00
|
|
|
#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;
|
2024-02-29 22:31:44 +00:00
|
|
|
bool modal;
|
2024-02-29 04:48:22 +00:00
|
|
|
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;
|
|
|
|
|
2024-02-29 22:31:44 +00:00
|
|
|
void game_dialogue_display(const char* text, bool modal) {
|
2024-02-29 04:48:22 +00:00
|
|
|
game_dialogue_screen.visible = true;
|
2024-02-29 22:31:44 +00:00
|
|
|
game_dialogue_screen.modal = modal;
|
2024-02-29 04:48:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-29 22:31:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool game_dialogue_is_busy() {
|
|
|
|
return game_dialogue_screen.visible;
|
|
|
|
}
|
|
|
|
|
2024-02-29 04:48:22 +00:00
|
|
|
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;
|
|
|
|
|
2024-02-29 22:31:44 +00:00
|
|
|
if (game_dialogue_screen.modal) {
|
|
|
|
y = DEVICE_H / 2 - game_dialogue_screen.h / 2;
|
|
|
|
}
|
2024-02-29 04:48:22 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2024-02-29 22:31:44 +00:00
|
|
|
sys_i32 cx = (x0 + x1)/2;
|
|
|
|
sys_i32 cy = (y0 + y1)/2;
|
|
|
|
|
2024-02-29 04:48:22 +00:00
|
|
|
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,
|
2024-02-29 22:31:44 +00:00
|
|
|
game_dialogue_screen.modal ? 0 : 1
|
2024-02-29 04:48:22 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2024-02-29 22:31:44 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-29 04:48:22 +00:00
|
|
|
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);
|
|
|
|
}
|