Speech bubbles

This commit is contained in:
Pyrex 2024-02-28 20:48:22 -08:00
parent 7e67d09508
commit c7d9f16515
8 changed files with 193 additions and 9 deletions

View File

@ -29,10 +29,17 @@ void game_update() {
sys_update();
game_frame += 1;
bool allow_input = true;
if (sys_btn(DEVICE_BUTTON_2)) {
game_dialogue_display("Hello, world!\nYou rock.");
}
game_collectibles_update();
game_npcs_update();
game_player_update();
game_dialogue_update(&allow_input);
game_player_update(&allow_input);
}
void game_draw() {
@ -46,4 +53,6 @@ void game_draw() {
game_player_draw();
sys_camera_reset();
game_dialogue_draw();
}

View File

@ -3,6 +3,7 @@
#include "game_collectible.h"
#include "game_collision.h"
#include "game_dialogue.h"
#include "game_math.h"
#include "game_npc.h"
#include "game_palette.h"

138
game/game_dialogue.c Normal file
View File

@ -0,0 +1,138 @@
#include <assert.h>
#include <stdbool.h>
#include "device/device.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;
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) {
game_dialogue_screen.visible = true;
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;
}
}
}
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;
sys_i32 cx = DEVICE_W / 2;
sys_i32 cy = DEVICE_H - game_dialogue_screen.h / 2 - 8;
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_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,
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;
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);
}

11
game/game_dialogue.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef GAME_DIALOGUE_H
#define GAME_DIALOGUE_H
#include <stdbool.h>
void game_dialogue_display(const char* text);
void game_dialogue_update(bool* allow_input);
void game_dialogue_draw();
#endif // GAME_DIALOGUE_H

View File

@ -37,7 +37,7 @@ void game_player_init() {
game_player_bbox.y = game_map_player_start_y;
}
void game_player_update() {
void game_player_update(bool* allow_input) {
game_collision collision = game_collision_move_to_contact(
&game_player_bbox,
game_player_dx,
@ -63,8 +63,8 @@ void game_player_update() {
}
int wanted_dx = game_player_dx;
if (sys_btn(DEVICE_BUTTON_L)) { wanted_dx -= 0x50; }
if (sys_btn(DEVICE_BUTTON_R)) { wanted_dx += 0x50; }
if (*allow_input && sys_btn(DEVICE_BUTTON_L)) { wanted_dx -= 0x50; }
if (*allow_input && sys_btn(DEVICE_BUTTON_R)) { wanted_dx += 0x50; }
// allow this if grounded or if it's not a change of direction
if (
@ -82,7 +82,7 @@ void game_player_update() {
}
if (game_player_grounded || game_player_coyote_frames > 0) {
if (sys_btnp(DEVICE_BUTTON_U, false)) {
if (*allow_input && sys_btnp(DEVICE_BUTTON_U, false)) {
game_player_dy = -0x200;
game_player_jump_frames = 13;
game_player_grounded = false;

View File

@ -2,7 +2,7 @@
#define GAME_PLAYER_H
void game_player_init();
void game_player_update();
void game_player_update(bool* allow_input);
void game_player_set_camera();
void game_player_draw();

View File

@ -57,7 +57,26 @@ sys_color sys_pixel_get(sys_i32 x, sys_i32 y) {
return device_pixels[y][x];
}
void sys_print(char* str, sys_i32 x, sys_i32 y, sys_color col) {
void sys_measure_text(const char* str, sys_i32* w, sys_i32* h) {
sys_i32 x = 0;
sys_i32 y = 0;
sys_i32 max_x = 0;
for (sys_i32 i = 0;; i++) {
max_x = sys_max_i32(x, max_x);
uint8_t c = str[i];
if (c == 0) { break; }
if (c == '\n') { x = 0; y += 8; continue; }
if (c == '\r') { x = 0; continue; }
x += 8;
}
*w = max_x;
*h = y + 8;
}
void sys_print(const char* str, sys_i32 x, sys_i32 y, sys_color col) {
x += sys_cam_dx;
y += sys_cam_dy;

View File

@ -54,10 +54,16 @@ sys_color sys_pixel_get(
// TODO: SSET/SGET
// TODO: FGET/FSET
/**
* Output the dimensions of the text (x and y)
*/
void sys_measure_text(const char* str, sys_i32* x, sys_i32* y);
/**
* Print a string `str` in the color `col`
*/
void sys_print(char* str, sys_i32 x, sys_i32 y, sys_color col);
void sys_print(const char* str, sys_i32 x, sys_i32 y, sys_color col);
// TODO: CURSOR? COLOR?
/**