Compare commits
14 Commits
c5c677320b
...
main
Author | SHA1 | Date | |
---|---|---|---|
6c25396d4b | |||
0a3a8cb8ab | |||
1a89e4f3b6 | |||
60e1a4ed43 | |||
c7d9f16515 | |||
7e67d09508 | |||
f5f5e2c20b | |||
567db0bd71 | |||
df1d1450e7 | |||
7649a2dc7e | |||
d084e4dba3 | |||
d6db2f3e5f | |||
b0f4106d51 | |||
84b7a09383 |
37
game/BUILD
37
game/BUILD
@@ -1,44 +1,27 @@
|
||||
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
|
||||
load("helpers.bzl", "add_sprites")
|
||||
|
||||
cc_library(
|
||||
name = "game",
|
||||
srcs = glob(["*.c"]) + [
|
||||
"art/game_collectibles.c",
|
||||
"art/game_hud.c",
|
||||
"art/game_npcs.c",
|
||||
"art/game_player.c",
|
||||
"art/game_tiles.c",
|
||||
"map/game_map.c",
|
||||
"map/game_map_entities.c",
|
||||
],
|
||||
hdrs = glob(["*.h", "art/*.h", "map/*.h"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//device:device", "//sys:sys"]
|
||||
)
|
||||
|
||||
run_binary(
|
||||
name = "game_player",
|
||||
args = [
|
||||
"game_player",
|
||||
"96", # n sprites
|
||||
"0", # key color
|
||||
"$(location :art/game_player.png)",
|
||||
"$(location :art/game_player.c)"
|
||||
],
|
||||
srcs = [":art/game_player.png"],
|
||||
outs = [":art/game_player.c"],
|
||||
tool = "//pytools:spritesheet",
|
||||
)
|
||||
|
||||
run_binary(
|
||||
name = "game_tiles",
|
||||
args = [
|
||||
"game_tiles",
|
||||
"120", # n sprites
|
||||
"0", # key color
|
||||
"$(location :art/game_tiles.png)",
|
||||
"$(location :art/game_tiles.c)"
|
||||
],
|
||||
srcs = [":art/game_tiles.png"],
|
||||
outs = [":art/game_tiles.c"],
|
||||
tool = "//pytools:spritesheet",
|
||||
)
|
||||
add_sprites(name="game_collectibles", n=24)
|
||||
add_sprites(name="game_hud", n=2)
|
||||
add_sprites(name="game_player", n=96)
|
||||
add_sprites(name="game_npcs", n=64)
|
||||
add_sprites(name="game_tiles", n=120)
|
||||
|
||||
run_binary(
|
||||
name = "game_map",
|
||||
|
BIN
game/art/game_collectibles.aseprite
Normal file
BIN
game/art/game_collectibles.aseprite
Normal file
Binary file not shown.
8
game/art/game_collectibles.h
Normal file
8
game/art/game_collectibles.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GAME_COLLECTIBLES_H
|
||||
#define GAME_COLLECTIBLES_H
|
||||
|
||||
#include "sys/sys.h"
|
||||
|
||||
extern sys_spritesheet spr_game_collectibles;
|
||||
|
||||
#endif // GAME_COLLECTIBLES_H
|
BIN
game/art/game_collectibles.png
Normal file
BIN
game/art/game_collectibles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 419 B |
BIN
game/art/game_hud.aseprite
Normal file
BIN
game/art/game_hud.aseprite
Normal file
Binary file not shown.
8
game/art/game_hud.h
Normal file
8
game/art/game_hud.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GAME_HUD_H
|
||||
#define GAME_HUD_H
|
||||
|
||||
#include "sys/sys.h"
|
||||
|
||||
extern sys_spritesheet spr_game_hud;
|
||||
|
||||
#endif // ART_GAME_HUD_H
|
BIN
game/art/game_hud.png
Normal file
BIN
game/art/game_hud.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
game/art/game_npcs.aseprite
Normal file
BIN
game/art/game_npcs.aseprite
Normal file
Binary file not shown.
8
game/art/game_npcs.h
Normal file
8
game/art/game_npcs.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef GAME_NPCS_H
|
||||
#define GAME_NPCS_H
|
||||
|
||||
#include "sys/sys.h"
|
||||
|
||||
extern sys_spritesheet spr_game_npcs;
|
||||
|
||||
#endif // GAME_NPCS_H
|
BIN
game/art/game_npcs.png
Normal file
BIN
game/art/game_npcs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 623 B |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 603 B |
22
game/game.c
22
game/game.c
@@ -15,6 +15,8 @@ const char* game_title() {
|
||||
void game_init() {
|
||||
sys_init();
|
||||
|
||||
map_game_map_create_entities();
|
||||
|
||||
game_palette_init();
|
||||
game_player_init();
|
||||
}
|
||||
@@ -27,13 +29,29 @@ void game_update() {
|
||||
sys_update();
|
||||
|
||||
game_frame += 1;
|
||||
game_player_update();
|
||||
|
||||
bool allow_input = true;
|
||||
|
||||
game_collectibles_update();
|
||||
game_dialogue_update(&allow_input);
|
||||
game_inflict_update(&allow_input);
|
||||
game_npcs_update(&allow_input);
|
||||
game_player_update(&allow_input);
|
||||
}
|
||||
|
||||
void game_draw() {
|
||||
sys_cls(9);
|
||||
|
||||
sys_map_draw(map_game_map, spr_game_tiles, 0, 0, 0, 0, 32, 18);
|
||||
game_player_set_camera();
|
||||
sys_map_draw(map_game_map, spr_game_tiles, 0, 0, 0, 0, map_game_map.width, map_game_map.height);
|
||||
|
||||
game_collectibles_draw();
|
||||
game_npcs_draw();
|
||||
game_player_draw();
|
||||
|
||||
sys_camera_reset();
|
||||
|
||||
game_player_draw_hud();
|
||||
game_inflict_draw();
|
||||
game_dialogue_draw();
|
||||
}
|
||||
|
@@ -1,7 +1,12 @@
|
||||
#ifndef CROCPARTY_GAME_H
|
||||
#define CROCPARTY_GAME_H
|
||||
|
||||
#include "game_collectible.h"
|
||||
#include "game_collision.h"
|
||||
#include "game_dialogue.h"
|
||||
#include "game_inflict.h"
|
||||
#include "game_math.h"
|
||||
#include "game_npc.h"
|
||||
#include "game_palette.h"
|
||||
#include "game_player.h"
|
||||
|
||||
|
114
game/game_collectible.c
Normal file
114
game/game_collectible.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <assert.h>
|
||||
#include "game.h"
|
||||
#include "art/game_collectibles.h"
|
||||
|
||||
// If this isn't enough, raise the number
|
||||
#define GAME_COLLECTIBLES_N 32
|
||||
|
||||
sys_i32 game_collectible_next = 0;
|
||||
game_collectible game_collectibles[GAME_COLLECTIBLES_N];
|
||||
|
||||
struct {
|
||||
sys_i32 x;
|
||||
sys_i32 y;
|
||||
sys_i32 r;
|
||||
} game_collectible_bubble = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.r = 0
|
||||
};
|
||||
|
||||
sys_i32 game_collectible_wobb_frame = 0;
|
||||
|
||||
void game_collectible_create(sys_i32 x, sys_i32 y, game_collectible_type type) {
|
||||
assert(game_collectible_next < GAME_COLLECTIBLES_N && "too many collectibles");
|
||||
|
||||
sys_i32 id = game_collectible_next++;
|
||||
game_collectibles[id].x = x;
|
||||
game_collectibles[id].y = y;
|
||||
game_collectibles[id].type = type;
|
||||
}
|
||||
|
||||
sys_i32 game_collectible_wobb() {
|
||||
return game_wobb3(game_collectible_wobb_frame & 0xff);
|
||||
}
|
||||
|
||||
void game_collectibles_update() {
|
||||
game_collectible_wobb_frame += 1;
|
||||
game_collectible_bubble.r = game_collectible_bubble.r * 7 / 8;
|
||||
if (game_collectible_bubble.r < PIXEL_SZ_MICROPIXEL / 2) { game_collectible_bubble.r = 0; }
|
||||
|
||||
// check collision with player
|
||||
sys_i32 player_x, player_y;
|
||||
game_player_get_center(&player_x, &player_y);
|
||||
|
||||
for (sys_i32 i = 0; i < game_collectible_next; i++) {
|
||||
sys_i32 r;
|
||||
game_collectible* c = &game_collectibles[i];
|
||||
|
||||
if (c->collected) { continue; }
|
||||
|
||||
switch (c->type) {
|
||||
case GAME_COLLECTIBLE_TYPE_CAKE: r = 16; break;
|
||||
default: r = 8; break;
|
||||
}
|
||||
|
||||
// convert to micropixels
|
||||
// add approx radius of player
|
||||
sys_i32 r_distcheck = (r + 5) * PIXEL_SZ_MICROPIXEL;
|
||||
|
||||
int64_t dx = player_x - c->x;
|
||||
int64_t dy = player_y - c->y;
|
||||
|
||||
if (dx * dx + dy * dy <= r_distcheck * r_distcheck) {
|
||||
c->collected = true;
|
||||
// TODO: Spawn effect
|
||||
game_collectible_bubble.x=c->x / PIXEL_SZ_MICROPIXEL;
|
||||
game_collectible_bubble.y=c->y / PIXEL_SZ_MICROPIXEL + game_collectible_wobb();
|
||||
game_collectible_bubble.r=r * PIXEL_SZ_MICROPIXEL;
|
||||
|
||||
switch (c->type) {
|
||||
case GAME_COLLECTIBLE_TYPE_CAKE: game_player_collectibles.n_cake += 16; break;
|
||||
case GAME_COLLECTIBLE_TYPE_MONEY_BIG: game_player_collectibles.n_dollars += 5; break;
|
||||
case GAME_COLLECTIBLE_TYPE_MONEY_SMALL: game_player_collectibles.n_dollars += 1; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_collectibles_draw() {
|
||||
if (game_collectible_bubble.r > 0) {
|
||||
sys_circ_fill(
|
||||
game_collectible_bubble.x,
|
||||
game_collectible_bubble.y,
|
||||
game_collectible_bubble.r / PIXEL_SZ_MICROPIXEL,
|
||||
7
|
||||
);
|
||||
}
|
||||
for (sys_i32 i = 0; i < game_collectible_next; i++) {
|
||||
game_collectible* c = &game_collectibles[i];
|
||||
|
||||
if (c->collected) { continue; }
|
||||
|
||||
sys_i32 x = c->x / PIXEL_SZ_MICROPIXEL;
|
||||
sys_i32 y = c->y / PIXEL_SZ_MICROPIXEL + game_collectible_wobb();
|
||||
switch(c->type) {
|
||||
case GAME_COLLECTIBLE_TYPE_CAKE:
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_collectibles,0,x-16,y-16,4,4,false,false);
|
||||
break;
|
||||
case GAME_COLLECTIBLE_TYPE_MONEY_BIG:
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_collectibles,4,x-8,y-8,2,2,false,false);
|
||||
break;
|
||||
case GAME_COLLECTIBLE_TYPE_MONEY_SMALL:
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_collectibles,16,x-8,y-8,2,2,false,false);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false&&"invalid collectible");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
game/game_collectible.h
Normal file
25
game/game_collectible.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef GAME_COLLECTIBLE_H
|
||||
#define GAME_COLLECTIBLE_H
|
||||
|
||||
#include "sys/sys.h"
|
||||
|
||||
typedef enum {
|
||||
GAME_COLLECTIBLE_TYPE_CAKE,
|
||||
GAME_COLLECTIBLE_TYPE_MONEY_BIG,
|
||||
GAME_COLLECTIBLE_TYPE_MONEY_SMALL,
|
||||
GAME_COLLECTIBLE_TYPE_N
|
||||
} game_collectible_type;
|
||||
|
||||
typedef struct {
|
||||
sys_i32 x;
|
||||
sys_i32 y;
|
||||
game_collectible_type type;
|
||||
bool collected;
|
||||
} game_collectible;
|
||||
|
||||
void game_collectible_create(sys_i32 x, sys_i32 y, game_collectible_type type);
|
||||
|
||||
void game_collectibles_update();
|
||||
void game_collectibles_draw();
|
||||
|
||||
#endif // GAME_COLLECTIBLE_H
|
@@ -16,9 +16,8 @@ bool game_collision_is_occlusive(sys_maptile tile) {
|
||||
|
||||
bool game_collision_can_move(game_bbox body, sys_i32 dx, sys_i32 dy) {
|
||||
// First: place the body in space.
|
||||
// Align its top-left corner to the pixel boundary
|
||||
sys_i32 x = body.x + dx;
|
||||
sys_i32 y = body.y + dy;
|
||||
sys_i32 x = body.x - body.x_origin + dx;
|
||||
sys_i32 y = body.y - body.y_origin + dy;
|
||||
|
||||
// this is extremely similar to the logic in move_to_contact
|
||||
// but has different rounding behavior
|
||||
@@ -80,16 +79,17 @@ game_collision game_collision_move_to_contact(
|
||||
}
|
||||
|
||||
// figure out where we are
|
||||
sys_i32 body_x0 = body->x;
|
||||
sys_i32 body_y0 = body->y;
|
||||
sys_i32 body_x1 = body->x + body->w;
|
||||
sys_i32 body_y1 = body->y + body->h;
|
||||
sys_i32 body_x0 = body->x - body->x_origin;
|
||||
sys_i32 body_y0 = body->y - body->y_origin;
|
||||
sys_i32 body_x1 = body_x0 + body->w;
|
||||
sys_i32 body_y1 = body_y0 + body->h;
|
||||
|
||||
// figure out what tile we're still in
|
||||
sys_i32 tile_x0 = round_down(body_x0, TILE_SZ_MICROPIXEL);
|
||||
sys_i32 tile_y0 = round_down(body_y0, TILE_SZ_MICROPIXEL);
|
||||
sys_i32 tile_x1 = round_up(body_x1, TILE_SZ_MICROPIXEL);
|
||||
sys_i32 tile_y1 = round_up(body_y1, TILE_SZ_MICROPIXEL);
|
||||
sys_i32 max_overlap = PIXEL_SZ_MICROPIXEL - 1;
|
||||
sys_i32 tile_x1 = round_up(body_x1 - max_overlap, TILE_SZ_MICROPIXEL) + max_overlap;
|
||||
sys_i32 tile_y1 = round_up(body_y1 - max_overlap, TILE_SZ_MICROPIXEL) + max_overlap;
|
||||
|
||||
// but we want the _next_ tile
|
||||
if (tile_x0 == body_x0) { tile_x0 -= TILE_SZ_MICROPIXEL; }
|
||||
|
@@ -8,6 +8,8 @@
|
||||
#define PIXEL_SZ_MICROPIXEL 0x100
|
||||
|
||||
typedef struct {
|
||||
sys_i32 x_origin;
|
||||
sys_i32 y_origin;
|
||||
sys_i32 x;
|
||||
sys_i32 y;
|
||||
sys_i32 w;
|
||||
|
158
game/game_dialogue.c
Normal file
158
game/game_dialogue.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#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);
|
||||
}
|
12
game/game_dialogue.h
Normal file
12
game/game_dialogue.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef GAME_DIALOGUE_H
|
||||
#define GAME_DIALOGUE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void game_dialogue_display(const char* text, bool modal);
|
||||
bool game_dialogue_is_busy();
|
||||
|
||||
void game_dialogue_update(bool* allow_input);
|
||||
void game_dialogue_draw();
|
||||
|
||||
#endif // GAME_DIALOGUE_H
|
119
game/game_inflict.c
Normal file
119
game/game_inflict.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "game.h"
|
||||
#include "device/device.h"
|
||||
|
||||
#define GAME_INFLICT_SCREEN_MAX_N_MODALS 4
|
||||
|
||||
typedef enum {
|
||||
GAME_INFLICT_SCREEN_STATE_WAIT_FOR_DIALOGUE,
|
||||
GAME_INFLICT_SCREEN_STATE_CLOSE_EYELIDS,
|
||||
GAME_INFLICT_SCREEN_STATE_PERFORM_MODALS,
|
||||
GAME_INFLICT_SCREEN_STATE_OPEN_EYELIDS,
|
||||
GAME_INFLICT_SCREEN_STATE_INACTIVE
|
||||
} game_inflict_screen_state_t;
|
||||
|
||||
game_inflict_screen_state_t game_inflict_screen_state =
|
||||
GAME_INFLICT_SCREEN_STATE_INACTIVE;
|
||||
|
||||
game_inflict_type game_inflict_screen_type = 0;
|
||||
sys_i32 game_inflict_screen_n_modals = 0;
|
||||
sys_i32 game_inflict_screen_next_modal = 0;
|
||||
sys_i32 game_inflict_screen_progress = 0;
|
||||
|
||||
const char* game_inflict_screen_modals[GAME_INFLICT_SCREEN_MAX_N_MODALS];
|
||||
|
||||
void game_inflict_apply();
|
||||
|
||||
void game_inflict_update(bool* allow_input) {
|
||||
if (game_inflict_screen_state <= GAME_INFLICT_SCREEN_STATE_OPEN_EYELIDS) {
|
||||
*allow_input = false;
|
||||
}
|
||||
|
||||
switch (game_inflict_screen_state) {
|
||||
case GAME_INFLICT_SCREEN_STATE_WAIT_FOR_DIALOGUE:
|
||||
if (!game_dialogue_is_busy()) {
|
||||
game_inflict_screen_state = GAME_INFLICT_SCREEN_STATE_CLOSE_EYELIDS;
|
||||
game_inflict_screen_progress = 0;
|
||||
}
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_CLOSE_EYELIDS:
|
||||
game_inflict_screen_progress += 10;
|
||||
if (game_inflict_screen_progress >= 0x100) {
|
||||
game_inflict_screen_state =
|
||||
GAME_INFLICT_SCREEN_STATE_PERFORM_MODALS;
|
||||
game_inflict_screen_progress = 0;
|
||||
game_inflict_apply();
|
||||
}
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_PERFORM_MODALS:
|
||||
if (game_dialogue_is_busy()) {
|
||||
// fine, don't do anything
|
||||
} else if (game_inflict_screen_next_modal < game_inflict_screen_n_modals) {
|
||||
game_dialogue_display(game_inflict_screen_modals[
|
||||
game_inflict_screen_next_modal++
|
||||
], true);
|
||||
} else {
|
||||
game_inflict_screen_state =
|
||||
GAME_INFLICT_SCREEN_STATE_OPEN_EYELIDS;
|
||||
}
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_OPEN_EYELIDS:
|
||||
game_inflict_screen_progress += 10;
|
||||
if (game_inflict_screen_progress >= 0x100) {
|
||||
game_inflict_screen_state =
|
||||
GAME_INFLICT_SCREEN_STATE_INACTIVE;
|
||||
game_inflict_screen_progress = 0;
|
||||
}
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_INACTIVE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void game_inflict_enqueue(int inflict_id) {
|
||||
game_inflict_screen_state = GAME_INFLICT_SCREEN_STATE_WAIT_FOR_DIALOGUE;
|
||||
|
||||
switch (inflict_id) {
|
||||
case GAME_INFLICT_TYPE_CHICKEN:
|
||||
game_inflict_screen_type = GAME_INFLICT_TYPE_CHICKEN;
|
||||
game_inflict_screen_n_modals = 2;
|
||||
game_inflict_screen_next_modal = 0;
|
||||
game_inflict_screen_modals[0] = "For your birthday...";
|
||||
game_inflict_screen_modals[1] = "You're a chicken!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void game_inflict_apply() {
|
||||
switch (game_inflict_screen_type) {
|
||||
case GAME_INFLICT_TYPE_CHICKEN:
|
||||
game_player_collectibles.n_cake = 0;
|
||||
game_player_phase = GAME_PLAYER_PHASE_CHICKEN;
|
||||
}
|
||||
}
|
||||
|
||||
void game_inflict_draw_eyelids(uint8_t progress);
|
||||
void game_inflict_draw() {
|
||||
switch (game_inflict_screen_state) {
|
||||
case GAME_INFLICT_SCREEN_STATE_WAIT_FOR_DIALOGUE:
|
||||
// TODO: Screen distortion
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_CLOSE_EYELIDS:
|
||||
game_inflict_draw_eyelids(game_inflict_screen_progress & 0xff);
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_PERFORM_MODALS:
|
||||
game_inflict_draw_eyelids(255);
|
||||
break;
|
||||
case GAME_INFLICT_SCREEN_STATE_OPEN_EYELIDS:
|
||||
game_inflict_draw_eyelids(255 - game_inflict_screen_progress & 0xff);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void game_inflict_draw_eyelids(uint8_t progress) {
|
||||
sys_i32 offset = DEVICE_H / 2 -
|
||||
((255 - progress) * (255 - progress) * DEVICE_H/2) / (255 * 255);
|
||||
sys_rect_fill(0, 0, DEVICE_W, offset, 0);
|
||||
sys_rect_fill(0, DEVICE_H - offset, DEVICE_W, DEVICE_H, 0);
|
||||
}
|
19
game/game_inflict.h
Normal file
19
game/game_inflict.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef GAME_INFLICT_H
|
||||
#define GAME_INFLICT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "game.h"
|
||||
|
||||
typedef enum {
|
||||
// GAME_INFLICT_TYPE_CROC=1,
|
||||
GAME_INFLICT_TYPE_CHICKEN=2,
|
||||
// GAME_INFLICT_TYPE_BAT=3,
|
||||
// GAME_INFLICT_TYPE_ASLEEP=4,
|
||||
} game_inflict_type;
|
||||
|
||||
void game_inflict_update(bool* allow_input);
|
||||
void game_inflict_draw();
|
||||
|
||||
void game_inflict_enqueue(int inflict_id);
|
||||
|
||||
#endif // GAME_INFLICT_H
|
36
game/game_math.c
Normal file
36
game/game_math.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "game_math.h"
|
||||
#include "sys/sys.h"
|
||||
|
||||
// prog runs from 0 to 255
|
||||
|
||||
// except for game_wobb1, all of these are
|
||||
// generated with list(enumerate([round(sin(i/256 * pi * 2)*k) for i in range(256)]))
|
||||
// for k = various values
|
||||
|
||||
sys_i32 game_wobb1(uint8_t prog) {
|
||||
// return 0 or 1 in a roughly sinusoidal pattern
|
||||
if (prog<128) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sys_i32 game_wobb2(uint8_t prog) {
|
||||
// return -1, 0, 1 in a roughly sinusoidal pattern
|
||||
if (prog<22) return 0;
|
||||
if (prog<107) return 1;
|
||||
if (prog<150) return 0;
|
||||
if (prog<235) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sys_i32 game_wobb3(uint8_t prog) {
|
||||
// return -2, -1, 0, 1, 2 in a roughly sinusoidal pattern
|
||||
if (prog<11) return 0;
|
||||
if (prog<35) return 1;
|
||||
if (prog<94) return 2;
|
||||
if (prog<118) return 1;
|
||||
if (prog<139) return 0;
|
||||
if (prog<163) return -1;
|
||||
if (prog<222) return -2;
|
||||
if (prog<246) return -1;
|
||||
return 0;
|
||||
}
|
11
game/game_math.h
Normal file
11
game/game_math.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef GAME_MATH_H
|
||||
#define GAME_MATH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sys/sys.h"
|
||||
|
||||
sys_i32 game_wobb1(uint8_t prog);
|
||||
sys_i32 game_wobb2(uint8_t prog);
|
||||
sys_i32 game_wobb3(uint8_t prog);
|
||||
|
||||
#endif // GAME_MATH_H
|
171
game/game_npc.c
Normal file
171
game/game_npc.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <assert.h>
|
||||
#include "art/game_hud.h"
|
||||
#include "art/game_npcs.h"
|
||||
#include "device/device.h"
|
||||
#include "sys/sys.h"
|
||||
#include "game.h"
|
||||
|
||||
// If this isn't enough, raise the number
|
||||
#define GAME_NPCS_N 32
|
||||
|
||||
typedef struct {
|
||||
uint8_t main;
|
||||
// alt0 and alt1 are ramps
|
||||
uint8_t alt0_0; uint8_t alt0_1;
|
||||
uint8_t alt1_0; uint8_t alt1_1;
|
||||
uint8_t eye_0; uint8_t eye_1;
|
||||
uint8_t pupil;
|
||||
uint8_t adornment;
|
||||
uint8_t shine;
|
||||
} game_npc_palette;
|
||||
|
||||
game_npc_palette game_npc_palettes[5] = {
|
||||
{14, 13, 12, 6, 7, 6, 7, 0, 8, 7},
|
||||
{4, 2, 3, 6, 7, 9, 10, 0, 4, 7},
|
||||
{13, 0, 1, 1, 13, 11, 10, 0, 13, 7},
|
||||
{7, 14, 7, 14, 7, 14, 7, 0, 14, 7},
|
||||
{12, 1, 13, 12, 6, 8, 7, 0, 8, 7}
|
||||
};
|
||||
|
||||
#define GAME_NPC_N_PALETTES (sizeof(game_npc_palettes)/sizeof(game_npc_palettes[0]))
|
||||
|
||||
sys_i32 game_npc_next = 0;
|
||||
game_npc game_npcs[GAME_NPCS_N];
|
||||
|
||||
game_npc* game_npc_create(sys_i32 x, sys_i32 y) {
|
||||
assert(game_npc_next < GAME_NPCS_N && "too many NPCs");
|
||||
|
||||
sys_i32 id = game_npc_next++;
|
||||
game_npc npc = {
|
||||
.x = x,
|
||||
.y = y,
|
||||
|
||||
.sprite_id = 0,
|
||||
.palette_id = 0,
|
||||
.inflict_id = 0,
|
||||
.face_left = false,
|
||||
.flip = false,
|
||||
|
||||
.n_dialogues = 0,
|
||||
.no_cake_dialogue = NULL,
|
||||
/* .dialogues = { doesn't matter }, */
|
||||
|
||||
.present = true,
|
||||
.n_dialogues_seen = 0,
|
||||
.received_cake = false,
|
||||
};
|
||||
game_npcs[id] = npc;
|
||||
return &game_npcs[id];
|
||||
}
|
||||
|
||||
void game_npc_trigger_dialogue(sys_i32 npc_id);
|
||||
void game_npcs_update(bool* allow_input) {
|
||||
if (!*allow_input) { return; }
|
||||
|
||||
// check collision with player
|
||||
sys_i32 player_x, player_y;
|
||||
|
||||
game_player_get_center(&player_x, &player_y);
|
||||
for (sys_i32 i = 0; i < game_npc_next; i++) {
|
||||
game_npc* n = &game_npcs[i];
|
||||
|
||||
if (!n->present) { continue; }
|
||||
|
||||
sys_i32 r_distcheck = 21 * PIXEL_SZ_MICROPIXEL;
|
||||
|
||||
int64_t dx = player_x - n->x;
|
||||
int64_t dy = player_y - n->y;
|
||||
|
||||
if (
|
||||
sys_btnp(DEVICE_BUTTON_0, false) &&
|
||||
dx * dx + dy * dy <= r_distcheck * r_distcheck
|
||||
) {
|
||||
n->face_left = dx < 0;
|
||||
game_npc_trigger_dialogue(i);
|
||||
*allow_input = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_npc_trigger_dialogue(sys_i32 npc_id) {
|
||||
const char* dialogue;
|
||||
|
||||
// TODO: Make it possible for the player to have the cake
|
||||
game_npc* npc = &game_npcs[npc_id];
|
||||
|
||||
if (
|
||||
!npc->received_cake &&
|
||||
game_player_collectibles.n_cake <= 0 &&
|
||||
npc->no_cake_dialogue != NULL
|
||||
) {
|
||||
dialogue = npc->no_cake_dialogue;
|
||||
} else if (npc->n_dialogues_seen < npc->n_dialogues) {
|
||||
dialogue = npc->dialogues[npc->n_dialogues_seen++];
|
||||
} else if (npc->n_dialogues > 0) {
|
||||
dialogue = npc->dialogues[npc->n_dialogues - 1];
|
||||
} else {
|
||||
// nothing to do with this npc
|
||||
return;
|
||||
}
|
||||
|
||||
// give the NPC cake if possible
|
||||
if (!npc->received_cake && game_player_collectibles.n_cake > 0) {
|
||||
npc->received_cake = true;
|
||||
game_player_collectibles.n_cake -= 1;
|
||||
|
||||
if (npc->inflict_id != 0) {
|
||||
game_inflict_enqueue(npc->inflict_id);
|
||||
}
|
||||
}
|
||||
|
||||
game_dialogue_display(dialogue, false);
|
||||
}
|
||||
|
||||
void game_npcs_draw() {
|
||||
for (sys_i32 i = 0; i < game_npc_next; i++) {
|
||||
game_npc* n = &game_npcs[i];
|
||||
|
||||
if (!n->present) { continue; }
|
||||
|
||||
game_npc_palette palette =
|
||||
game_npc_palettes[n->palette_id % GAME_NPC_N_PALETTES];
|
||||
|
||||
if (n->received_cake) {
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_hud,
|
||||
0,
|
||||
n->x / PIXEL_SZ_MICROPIXEL - 4,
|
||||
n->y / PIXEL_SZ_MICROPIXEL - 18,
|
||||
1,
|
||||
1,
|
||||
!n->face_left,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
sys_dpal_set(14, palette.main);
|
||||
sys_dpal_set(13, palette.alt0_0);
|
||||
sys_dpal_set(12, palette.alt0_1);
|
||||
sys_dpal_set(6, palette.alt1_0);
|
||||
sys_dpal_set(7, palette.alt1_1);
|
||||
sys_dpal_set(9, palette.eye_0);
|
||||
sys_dpal_set(10, palette.eye_1);
|
||||
sys_dpal_set(1, palette.pupil);
|
||||
sys_dpal_set(8, palette.adornment);
|
||||
sys_dpal_set(11, palette.shine);
|
||||
|
||||
// TODO: Palette
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_npcs,
|
||||
n->sprite_id,
|
||||
n->x / PIXEL_SZ_MICROPIXEL - 8,
|
||||
n->y / PIXEL_SZ_MICROPIXEL - 8,
|
||||
2,
|
||||
2,
|
||||
n->face_left,
|
||||
n->flip
|
||||
);
|
||||
|
||||
sys_dpal_reset();
|
||||
}
|
||||
}
|
34
game/game_npc.h
Normal file
34
game/game_npc.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef GAME_NPC_H
|
||||
#define GAME_NPC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sys/sys.h"
|
||||
|
||||
#define GAME_NPC_MAX_N_DIALOGUES 16
|
||||
|
||||
typedef struct {
|
||||
sys_i32 x;
|
||||
sys_i32 y;
|
||||
|
||||
uint8_t sprite_id;
|
||||
uint8_t palette_id;
|
||||
uint8_t inflict_id;
|
||||
bool face_left;
|
||||
bool flip;
|
||||
|
||||
uint8_t n_dialogues;
|
||||
const char* no_cake_dialogue;
|
||||
const char* dialogues[GAME_NPC_MAX_N_DIALOGUES];
|
||||
|
||||
bool present;
|
||||
uint8_t n_dialogues_seen;
|
||||
bool received_cake;
|
||||
} game_npc;
|
||||
|
||||
// pointer goes to data section and will be valid forever
|
||||
game_npc* game_npc_create(sys_i32 x, sys_i32 y);
|
||||
|
||||
void game_npcs_update(bool* allow_input);
|
||||
void game_npcs_draw();
|
||||
|
||||
#endif // GAME_NPC_H
|
@@ -1,3 +1,5 @@
|
||||
#include <assert.h>
|
||||
#include "art/game_hud.h"
|
||||
#include "art/game_player.h"
|
||||
#include "art/game_tiles.h"
|
||||
#include "device/device.h"
|
||||
@@ -5,14 +7,12 @@
|
||||
#include "game.h"
|
||||
#include "sys/sys.h"
|
||||
|
||||
#define GAME_PLAYER_ORIGIN_X 4
|
||||
#define GAME_PLAYER_ORIGIN_Y 4
|
||||
|
||||
game_bbox game_player_bbox = {
|
||||
.x_origin=0x0500,
|
||||
.y_origin=0x0c00,
|
||||
.x=0x9000,
|
||||
// .y=0x5800,
|
||||
.y=0x0000,
|
||||
.w=0x0800,
|
||||
.y=0x1000,
|
||||
.w=0x0a00,
|
||||
.h=0x0c00,
|
||||
};
|
||||
bool game_player_grounded = false;
|
||||
@@ -22,6 +22,13 @@ sys_i32 game_player_dx = 0;
|
||||
sys_i32 game_player_dy = 0;
|
||||
bool game_player_faceleft = false;
|
||||
|
||||
game_player_collectibles_t game_player_collectibles = {
|
||||
.n_cake=0,
|
||||
.n_dollars=0
|
||||
};
|
||||
|
||||
game_player_phase_t game_player_phase = GAME_PLAYER_PHASE_CROC;
|
||||
|
||||
typedef enum {
|
||||
GAME_PLAYER_ANIM_STANDING,
|
||||
GAME_PLAYER_ANIM_WALKING,
|
||||
@@ -35,20 +42,41 @@ void game_player_anim_transition(game_player_anim anim);
|
||||
void game_player_anim_add_progress(uint8_t amt);
|
||||
|
||||
void game_player_init() {
|
||||
game_player_bbox.x = game_map_player_start_x;
|
||||
game_player_bbox.y = game_map_player_start_y;
|
||||
}
|
||||
|
||||
void game_player_update() {
|
||||
void game_player_update(bool* allow_input) {
|
||||
sys_i32 dx_max, ddx;
|
||||
switch (game_player_phase) {
|
||||
case GAME_PLAYER_PHASE_CROC:
|
||||
dx_max = 0x140;
|
||||
ddx = 0x50;
|
||||
game_player_bbox.x_origin=0x500;
|
||||
game_player_bbox.w = 0xa00;
|
||||
game_player_bbox.y_origin = game_player_bbox.h = 0xa00; break;
|
||||
break;
|
||||
case GAME_PLAYER_PHASE_CHICKEN:
|
||||
dx_max = 0xd0;
|
||||
ddx = 0x30;
|
||||
game_player_bbox.x_origin=0x300;
|
||||
game_player_bbox.w = 0x600;
|
||||
game_player_bbox.y_origin = game_player_bbox.h = 0x700; break;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
game_collision collision = game_collision_move_to_contact(
|
||||
&game_player_bbox,
|
||||
game_player_dx,
|
||||
game_player_dy
|
||||
);
|
||||
|
||||
|
||||
if (collision.collided_x) { game_player_dx = 0; }
|
||||
if (collision.collided_y) { game_player_dy = 0; }
|
||||
|
||||
game_player_grounded = !game_collision_can_move(
|
||||
game_player_bbox, 0, PIXEL_SZ_MICROPIXEL
|
||||
game_player_bbox, 0, 1
|
||||
);
|
||||
|
||||
{
|
||||
@@ -58,13 +86,13 @@ void game_player_update() {
|
||||
// round up
|
||||
0x7f * sys_sgn_i32(game_player_dx)
|
||||
) / 0x80;
|
||||
if (sys_abs_i32(game_player_dx) > 0x200) {
|
||||
game_player_dx = sys_sgn_i32(game_player_dx) * 0x200;
|
||||
if (sys_abs_i32(game_player_dx) > dx_max) {
|
||||
game_player_dx = sys_sgn_i32(game_player_dx) * dx_max;
|
||||
}
|
||||
|
||||
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 -= ddx; }
|
||||
if (*allow_input && sys_btn(DEVICE_BUTTON_R)) { wanted_dx += ddx; }
|
||||
|
||||
// allow this if grounded or if it's not a change of direction
|
||||
if (
|
||||
@@ -82,9 +110,9 @@ void game_player_update() {
|
||||
}
|
||||
|
||||
if (game_player_grounded || game_player_coyote_frames > 0) {
|
||||
if (sys_btnp(DEVICE_BUTTON_U, false)) {
|
||||
game_player_dy = -0x300;
|
||||
game_player_jump_frames = 15;
|
||||
if (*allow_input && sys_btnp(DEVICE_BUTTON_U, false)) {
|
||||
game_player_dy = -0x200;
|
||||
game_player_jump_frames = 13;
|
||||
game_player_grounded = false;
|
||||
}
|
||||
}
|
||||
@@ -129,26 +157,55 @@ void game_player_update() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void game_player_get_center(sys_i32* x, sys_i32* y) {
|
||||
*x = game_player_bbox.x;
|
||||
|
||||
// center on me, not on feet
|
||||
*y = game_player_bbox.y - 8 * PIXEL_SZ_MICROPIXEL;
|
||||
}
|
||||
|
||||
void game_player_set_camera() {
|
||||
sys_i32 x = game_player_bbox.x;
|
||||
sys_i32 y = game_player_bbox.y;
|
||||
|
||||
// TODO: Use round_down et al
|
||||
sys_i32 x_pixel = x / PIXEL_SZ_MICROPIXEL;
|
||||
sys_i32 y_pixel = y / PIXEL_SZ_MICROPIXEL;
|
||||
|
||||
sys_i32 x_topcorner = x_pixel - x_pixel % DEVICE_W;
|
||||
sys_i32 y_topcorner = y_pixel - y_pixel % DEVICE_H;
|
||||
|
||||
sys_camera_set(x_topcorner, y_topcorner);
|
||||
}
|
||||
|
||||
void game_player_draw() {
|
||||
int game_player_image;
|
||||
uint8_t game_player_image;
|
||||
|
||||
uint8_t base = 0;
|
||||
switch (game_player_phase) {
|
||||
default: case GAME_PLAYER_PHASE_CROC: base = 0; break;
|
||||
case GAME_PLAYER_PHASE_CHICKEN: base = 32; break;
|
||||
}
|
||||
|
||||
switch (game_player_anim_state) {
|
||||
case GAME_PLAYER_ANIM_WALKING:
|
||||
game_player_image = 2 + (game_player_anim_progress / 0x80) * 2;
|
||||
game_player_image = base + 2 + (game_player_anim_progress / 0x80) * 2;
|
||||
break;
|
||||
case GAME_PLAYER_ANIM_FLOATING:
|
||||
game_player_image = 6;
|
||||
game_player_image = base + 6;
|
||||
break;
|
||||
default:
|
||||
case GAME_PLAYER_ANIM_STANDING:
|
||||
game_player_image = 0;
|
||||
game_player_image = base + 0;
|
||||
break;
|
||||
}
|
||||
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_player,
|
||||
game_player_image,
|
||||
game_player_bbox.x / 0x100 - GAME_PLAYER_ORIGIN_X,
|
||||
game_player_bbox.y / 0x100 - GAME_PLAYER_ORIGIN_Y,
|
||||
game_player_bbox.x / 0x100 - 8,
|
||||
game_player_bbox.y / 0x100 - 16,
|
||||
2, 2,
|
||||
game_player_faceleft, false
|
||||
);
|
||||
@@ -166,3 +223,67 @@ void game_player_anim_add_progress(uint8_t amt) {
|
||||
(uint32_t) amt
|
||||
) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
void game_player_draw_hud_number(sys_i32 number, sys_i32 x, sys_i32 y);
|
||||
void game_player_draw_hud() {
|
||||
sys_i32 y = 4;
|
||||
if (game_player_collectibles.n_dollars > 0) {
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_hud,
|
||||
1,
|
||||
4, y,
|
||||
1, 1,
|
||||
false, false
|
||||
);
|
||||
game_player_draw_hud_number(game_player_collectibles.n_dollars, 16, y);
|
||||
y += 10;
|
||||
}
|
||||
if (game_player_collectibles.n_cake > 0) {
|
||||
sys_sprite_draw_ext(
|
||||
spr_game_hud,
|
||||
0,
|
||||
4, y - 1,
|
||||
1, 1,
|
||||
false, false
|
||||
);
|
||||
game_player_draw_hud_number(game_player_collectibles.n_cake, 16, y);
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void game_player_draw_hud_number1(sys_i32 number, sys_i32 x, sys_i32 y, uint8_t color) {
|
||||
number = sys_min_i32(sys_max_i32(number, 0), 255);
|
||||
|
||||
char s[2];
|
||||
s[1] = 0;
|
||||
|
||||
if (number >= 100) {
|
||||
sys_i32 top_digit = number / 100;
|
||||
assert(0 <= top_digit && top_digit < 10);
|
||||
s[0] = '0' + top_digit;
|
||||
number = number % 100;
|
||||
sys_print(s, x, y, color);
|
||||
x += 8;
|
||||
}
|
||||
if (number >= 10) {
|
||||
sys_i32 top_digit = number / 10;
|
||||
assert(0 <= top_digit && top_digit < 10);
|
||||
s[0] = '0' + top_digit;
|
||||
number = number % 10;
|
||||
sys_print(s, x, y, color);
|
||||
x += 8;
|
||||
}
|
||||
sys_i32 top_digit = number;
|
||||
assert(0 <= top_digit && top_digit < 10);
|
||||
s[0] = '0' + top_digit;
|
||||
sys_print(s, x, y, color);
|
||||
}
|
||||
void game_player_draw_hud_number(sys_i32 number, sys_i32 x_start, sys_i32 y_start) {
|
||||
for (sys_i32 dy = -1; dy <= 1; dy++) {
|
||||
for (sys_i32 dx = -1; dx <= 1; dx++) {
|
||||
game_player_draw_hud_number1(number, x_start+dx, y_start+dy, 0);
|
||||
}
|
||||
}
|
||||
game_player_draw_hud_number1(number, x_start, y_start, 7);
|
||||
}
|
@@ -1,8 +1,24 @@
|
||||
#ifndef GAME_PLAYER_H
|
||||
#define GAME_PLAYER_H
|
||||
|
||||
typedef struct {
|
||||
sys_i32 n_cake;
|
||||
sys_i32 n_dollars;
|
||||
} game_player_collectibles_t;
|
||||
extern game_player_collectibles_t game_player_collectibles;
|
||||
|
||||
typedef enum {
|
||||
GAME_PLAYER_PHASE_CROC = 0,
|
||||
GAME_PLAYER_PHASE_CHICKEN = 1,
|
||||
} game_player_phase_t;
|
||||
extern game_player_phase_t game_player_phase;
|
||||
|
||||
void game_player_init();
|
||||
void game_player_update();
|
||||
void game_player_update(bool* allow_input);
|
||||
void game_player_get_center(sys_i32* x, sys_i32* y);
|
||||
|
||||
void game_player_set_camera();
|
||||
void game_player_draw();
|
||||
void game_player_draw_hud();
|
||||
|
||||
#endif // GAME_PLAYER_H
|
16
game/helpers.bzl
Normal file
16
game/helpers.bzl
Normal file
@@ -0,0 +1,16 @@
|
||||
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
|
||||
|
||||
def add_sprites(name, n):
|
||||
run_binary(
|
||||
name = name,
|
||||
args = [
|
||||
name,
|
||||
"{}".format(n), # n sprites
|
||||
"0", # key color
|
||||
"$(location :art/{}.png)".format(name),
|
||||
"$(location :art/{}.c)".format(name)
|
||||
],
|
||||
srcs = [":art/{}.png".format(name)],
|
||||
outs = [":art/{}.c".format(name)],
|
||||
tool = "//pytools:spritesheet",
|
||||
)
|
@@ -1,11 +1,13 @@
|
||||
#ifndef CROCPARTY_GAME_MAP_H
|
||||
#define CROCPARTY_GAME_MAP_H
|
||||
|
||||
#include "game/game_collision.h"
|
||||
#include "sys/sys.h"
|
||||
|
||||
extern sys_i32 game_map_player_start_x;
|
||||
extern sys_i32 game_map_player_start_y;
|
||||
extern sys_map map_game_map;
|
||||
|
||||
void map_game_map_create_entities();
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
86
game/map/game_map_entities.c
Normal file
86
game/map/game_map_entities.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <assert.h>
|
||||
#include "sys/sys.h"
|
||||
#include "game/game.h"
|
||||
#include "game_map.h"
|
||||
|
||||
sys_i32 game_map_player_start_x;
|
||||
sys_i32 game_map_player_start_y;
|
||||
|
||||
// called by map_game_map_create_entities
|
||||
void map_game_map_player_spawn_create(sys_i32 x, sys_i32 y) {
|
||||
// center, because the entity has top-left positioning
|
||||
game_map_player_start_x = x * TILE_SZ_MICROPIXEL + PIXEL_SZ_MICROPIXEL * 8;
|
||||
game_map_player_start_y = y * TILE_SZ_MICROPIXEL + PIXEL_SZ_MICROPIXEL * 16;
|
||||
}
|
||||
|
||||
void map_game_map_collectible_cake_create(sys_i32 x, sys_i32 y) {
|
||||
game_collectible_create(
|
||||
(x + 2) * TILE_SZ_MICROPIXEL,
|
||||
(y + 2) * TILE_SZ_MICROPIXEL,
|
||||
GAME_COLLECTIBLE_TYPE_CAKE
|
||||
);
|
||||
}
|
||||
|
||||
void map_game_map_collectible_money_big_create(sys_i32 x, sys_i32 y) {
|
||||
game_collectible_create(
|
||||
(x + 1) * TILE_SZ_MICROPIXEL,
|
||||
(y + 1) * TILE_SZ_MICROPIXEL,
|
||||
GAME_COLLECTIBLE_TYPE_MONEY_BIG
|
||||
);
|
||||
}
|
||||
|
||||
void map_game_map_collectible_money_small_create(sys_i32 x, sys_i32 y) {
|
||||
game_collectible_create(
|
||||
(x + 1) * TILE_SZ_MICROPIXEL,
|
||||
(y + 1) * TILE_SZ_MICROPIXEL,
|
||||
GAME_COLLECTIBLE_TYPE_MONEY_SMALL
|
||||
);
|
||||
}
|
||||
|
||||
game_npc* game_map_npc_in_progress = NULL;
|
||||
|
||||
void map_game_map_npc_create(sys_i32 x, sys_i32 y) {
|
||||
// center NPC
|
||||
game_map_npc_in_progress = game_npc_create(
|
||||
(x + 1) * TILE_SZ_MICROPIXEL,
|
||||
(y + 1) * TILE_SZ_MICROPIXEL
|
||||
);
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_sprite_id(sys_i32 id) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->sprite_id = id;
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_palette_id(sys_i32 id) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->palette_id = id;
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_inflict_id(sys_i32 id) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->inflict_id = id;
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_face_left(bool face_left) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->face_left = face_left;
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_flip(bool flip) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->flip = flip;
|
||||
}
|
||||
|
||||
void map_game_map_npc_set_no_cake_dialogue(const char* dialogue) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
game_map_npc_in_progress->no_cake_dialogue = dialogue;
|
||||
}
|
||||
|
||||
void map_game_map_npc_add_dialogue(const char* dialogue) {
|
||||
assert(game_map_npc_in_progress && "NPC has to be in progress");
|
||||
assert(game_map_npc_in_progress->n_dialogues < GAME_NPC_MAX_N_DIALOGUES &&
|
||||
"NPC can't have too many dialogues");
|
||||
sys_i32 ix = game_map_npc_in_progress->n_dialogues++;
|
||||
game_map_npc_in_progress->dialogues[ix] = dialogue;
|
||||
}
|
@@ -1,9 +1,11 @@
|
||||
import sys
|
||||
import json
|
||||
import shared
|
||||
import textwrap
|
||||
|
||||
TEMPLATE = """
|
||||
// generated code! be nice
|
||||
#include <stdbool.h>
|
||||
#include "sys/sys.h"
|
||||
|
||||
sys_maptile map_{{map_name}}_data[{{width * height}}] = { {{ tiles|join(",") }} };
|
||||
@@ -12,10 +14,42 @@ sys_map map_{{map_name}} = {
|
||||
.width={{width}},
|
||||
.height={{height}},
|
||||
};
|
||||
|
||||
{% for entity_type in entity_types %}
|
||||
void map_{{map_name}}_{{entity_type}}_create(sys_i32 x, sys_i32 y);
|
||||
{% endfor %}
|
||||
|
||||
{% for entity_name, field_name, c_type, render_mode in entity_fields %}
|
||||
{% if render_mode == "scalar" %}
|
||||
void map_{{map_name}}_{{entity_name}}_set_{{field_name}}({{c_type}} value);
|
||||
{% elif render_mode == "array" %}
|
||||
void map_{{map_name}}_{{entity_name}}_add_{{field_name}}({{c_type}} value);
|
||||
{% else %}
|
||||
wtf; // {{ render_mode }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
void map_{{map_name}}_create_entities() {
|
||||
{% for entity in entities %}
|
||||
map_{{map_name}}_{{entity.type}}_create({{entity.x}}, {{entity.y}});
|
||||
|
||||
{% for field in entity.fields %}
|
||||
{% if field.renderMode == "scalar" %}
|
||||
map_{{map_name}}_{{entity.type}}_set_{{field.name}}({{field.value|safe}});
|
||||
{% elif field.renderMode == "array" %}
|
||||
{% for s in field.value %}
|
||||
map_{{map_name}}_{{entity.type}}_add_{{field.name}}({{s|safe}});
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
}
|
||||
""".lstrip()
|
||||
|
||||
def main(map_name, fname_ldtk, fname_c):
|
||||
width, height, tiles = load_mapdata(fname_ldtk)
|
||||
width, height, tiles, entities, entity_types, entity_fields = load_mapdata(fname_ldtk)
|
||||
print(entity_fields)
|
||||
|
||||
with open(fname_c, "wt") as output:
|
||||
output.write(
|
||||
@@ -24,6 +58,9 @@ def main(map_name, fname_ldtk, fname_c):
|
||||
tiles=tiles,
|
||||
width=width,
|
||||
height=height,
|
||||
entities=entities,
|
||||
entity_types=entity_types,
|
||||
entity_fields=entity_fields,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -31,6 +68,8 @@ def main(map_name, fname_ldtk, fname_c):
|
||||
def load_mapdata(fname_ldtk):
|
||||
sparse_tiles = {}
|
||||
entities = []
|
||||
entity_types = set()
|
||||
entity_fields = set()
|
||||
|
||||
with open(fname_ldtk, "rt") as f:
|
||||
data = json.load(f)
|
||||
@@ -49,7 +88,28 @@ def load_mapdata(fname_ldtk):
|
||||
|
||||
if layer["__identifier"] == "entities":
|
||||
for e in layer["entityInstances"]:
|
||||
raise NotImplementedError()
|
||||
# TODO: Other fields?
|
||||
entity = {
|
||||
"type": e["__identifier"],
|
||||
"x": e["__worldX"] // 8,
|
||||
"y": e["__worldY"] // 8,
|
||||
"fields": []
|
||||
}
|
||||
for f in e["fieldInstances"]:
|
||||
field = {
|
||||
"name": f["__identifier"],
|
||||
}
|
||||
field["value"], field["renderMode"], rendered_type = format_field_value(f["__type"], f["__value"])
|
||||
entity["fields"].append(field)
|
||||
entity_fields.add((
|
||||
entity["type"],
|
||||
field["name"],
|
||||
rendered_type,
|
||||
field["renderMode"]
|
||||
))
|
||||
|
||||
entities.append(entity)
|
||||
entity_types.add(entity["type"])
|
||||
|
||||
x_min = 0
|
||||
y_min = 0
|
||||
@@ -66,9 +126,29 @@ def load_mapdata(fname_ldtk):
|
||||
else:
|
||||
dense_tiles.append(255)
|
||||
|
||||
return width, height, dense_tiles
|
||||
return width, height, dense_tiles, entities, entity_types, entity_fields
|
||||
|
||||
|
||||
def format_field_value(ty, val):
|
||||
if ty == "Bool":
|
||||
return ("true" if val else "false"), "scalar", "bool"
|
||||
elif ty == "Int":
|
||||
return str(val), "scalar", "sys_i32"
|
||||
elif ty == "String":
|
||||
if val is None:
|
||||
return "NULL", "scalar", "const char*"
|
||||
return json.dumps(wrap(val)), "scalar", "const char*" # this is close enough to being right in C
|
||||
elif ty == "Array<Int>":
|
||||
return [format_field_value("Int", i)[0] for i in val], "array", "sys_i32"
|
||||
elif ty == "Array<String>":
|
||||
return [format_field_value("String", i)[0] for i in val], "array", "const char*"
|
||||
else:
|
||||
assert False, f"unknown type: {ty}"
|
||||
|
||||
|
||||
def wrap(s):
|
||||
return textwrap.fill(s, width=28)
|
||||
|
||||
def annot_xy(lst, w, h):
|
||||
assert len(lst) == w * h
|
||||
for y in range(h):
|
||||
|
@@ -57,7 +57,29 @@ 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;
|
||||
|
||||
sys_i32 x_orig = x;
|
||||
for (sys_i32 i = 0;; i++) {
|
||||
uint8_t c = str[i];
|
||||
@@ -135,11 +157,17 @@ void sys_oval_fill(
|
||||
void sys_oval_draw_ext(
|
||||
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c, bool fill
|
||||
) {
|
||||
x0 += sys_cam_dx;
|
||||
y0 += sys_cam_dy;
|
||||
x1 += sys_cam_dx;
|
||||
y1 += sys_cam_dy;
|
||||
|
||||
assert(sys_get_initialized());
|
||||
|
||||
if (x0 == x1 || y0 == y1) { return; }
|
||||
if (x0 > x1) { sys_i32 tmp = x0; x0 = x1; x1 = tmp; }
|
||||
if (y0 > y1) { sys_i32 tmp = y0; y0 = y1; y1 = tmp; }
|
||||
// TODO: Offset by 1px so that x0/y0 is always included?
|
||||
|
||||
// alois' algorithm for this implies the bounds are inclusive
|
||||
x1 -= 1; y1 -= 1;
|
||||
@@ -158,11 +186,9 @@ void sys_oval_draw_ext(
|
||||
int64_t bb8 = 8 * b * b;
|
||||
|
||||
do {
|
||||
if (!fill) {
|
||||
// draw the points at the edge of the line
|
||||
sys_scanline_internal_set(x0, x1, y0, c, false);
|
||||
sys_scanline_internal_set(x0, x1, y1, c, false);
|
||||
}
|
||||
// draw the points at the edge of the line no matter what
|
||||
sys_scanline_internal_set(x0, x1, y0, c, false);
|
||||
sys_scanline_internal_set(x0, x1, y1, c, false);
|
||||
int64_t e2 = 2 * err;
|
||||
if (e2 <= dy) {
|
||||
if (fill) {
|
||||
@@ -186,6 +212,11 @@ void sys_oval_draw_ext(
|
||||
void sys_line_draw(
|
||||
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
||||
) {
|
||||
x0 += sys_cam_dx;
|
||||
y0 += sys_cam_dy;
|
||||
x1 += sys_cam_dx;
|
||||
y1 += sys_cam_dy;
|
||||
|
||||
assert(sys_get_initialized());
|
||||
|
||||
if (x0 == x1 || y0 == y1) { return; }
|
||||
@@ -205,6 +236,43 @@ void sys_line_draw(
|
||||
}
|
||||
}
|
||||
|
||||
void sys_rect_draw(
|
||||
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
||||
) {
|
||||
sys_rect_draw_ext(x0, y0, x1, y1, c, false);
|
||||
}
|
||||
|
||||
void sys_rect_fill(
|
||||
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c
|
||||
) {
|
||||
sys_rect_draw_ext(x0, y0, x1, y1, c, true);
|
||||
}
|
||||
|
||||
void sys_rect_draw_ext(
|
||||
sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1, sys_color c, bool fill
|
||||
) {
|
||||
x0 += sys_cam_dx;
|
||||
y0 += sys_cam_dy;
|
||||
x1 += sys_cam_dx;
|
||||
y1 += sys_cam_dy;
|
||||
|
||||
assert(sys_get_initialized());
|
||||
|
||||
if (x0 == x1 || y0 == y1) { return; }
|
||||
|
||||
if (x0 == x1 || y0 == y1) { return; }
|
||||
if (x0 > x1) { sys_i32 tmp = x0; x0 = x1; x1 = tmp; }
|
||||
if (y0 > y1) { sys_i32 tmp = y0; y0 = y1; y1 = tmp; }
|
||||
// TODO: Offset by 1px so that x0/y0 is always included?
|
||||
|
||||
for (sys_i32 y = y0; y < y1; y++) {
|
||||
bool whole_line = y == y0 || y == y1;
|
||||
sys_scanline_internal_set(
|
||||
x0, x1, y, c, whole_line || fill
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void sys_spal_set(sys_color c0, sys_screen_color rc1) {
|
||||
assert(sys_get_initialized());
|
||||
|
||||
@@ -254,6 +322,9 @@ void sys_sprite_draw_ext(
|
||||
sys_i32 w, sys_i32 h,
|
||||
bool flip_x, bool flip_y
|
||||
) {
|
||||
x += sys_cam_dx;
|
||||
y += sys_cam_dy;
|
||||
|
||||
// map n to a specific entity on the spritesheet
|
||||
// (this is necessary for w and h)
|
||||
for (int sy = 0; sy < h; sy++) {
|
||||
@@ -283,6 +354,10 @@ void sys_map_draw(
|
||||
sys_i32 tile_x, sys_i32 tile_y,
|
||||
sys_i32 tile_w, sys_i32 tile_h
|
||||
) {
|
||||
// no need to do this, sys_sprite_draw does it
|
||||
// sx += sys_cam_dx;
|
||||
// sy += sys_cam_dy;
|
||||
|
||||
for (sys_i32 ty = 0; ty < tile_h; ty++) {
|
||||
for (sys_i32 tx = 0; tx < tile_w; tx++) {
|
||||
sys_i32 real_tx = tx + tile_x;
|
||||
|
@@ -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?
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user