commit d1abfae121fdca02aba9e0d6d9133c21a7da1e94
parent 9d6768ea94eeea7ccb242b77a35e7f82cbc6c21e
Author: beep <beep@wimdupont.com>
Date: Wed, 29 Jul 2026 11:49:13 +0000
Add inventory menu and spear pickup
Diffstat:
3 files changed, 104 insertions(+), 30 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -21,10 +21,10 @@
#define PH_NPC_COLOR 220, 180, 90, 255
#define PH_NOTICE_COLOR 20, 24, 34, 255
#define PH_NOTICE_TEXT_COLOR 232, 236, 244, 255
-#define PH_SHEET_COLOR 20, 24, 34, 255
-#define PH_SHEET_BORDER_COLOR 116, 126, 148, 255
-#define PH_SHEET_TEXT_COLOR 232, 236, 244, 255
-#define PH_SHEET_BONUS_COLOR 126, 220, 146, 255
+#define PH_MENU_COLOR 20, 24, 34, 255
+#define PH_MENU_BORDER_COLOR 116, 126, 148, 255
+#define PH_MENU_TEXT_COLOR 232, 236, 244, 255
+#define PH_MENU_BONUS_COLOR 126, 220, 146, 255
enum {
PH_ASSET_NONE,
@@ -55,7 +55,7 @@ enum {
};
enum {
- PH_ITEM_TALENT_SHARD = 1,
+ PH_ITEM_RUSTED_SPEAR = 1,
PH_ITEM_WAYFARER_BLADE,
PH_ITEM_TRAVELER_COAT,
};
@@ -83,10 +83,11 @@ static const PhEntityDef ph_entity_defs[] = {
static const PhItemDef ph_item_defs[] = {
{
- .id = PH_ITEM_TALENT_SHARD,
- .name = "Talent Shard",
- .value = 30,
- .equip_slot = PH_EQUIP_NONE,
+ .id = PH_ITEM_RUSTED_SPEAR,
+ .name = "Rusted Spear",
+ .value = 12,
+ .bonuses = { .attack = 2 },
+ .equip_slot = PH_EQUIP_WEAPON,
.asset_id = PH_ASSET_ITEMS,
.sprite_tile_x = 2,
.sprite_tile_y = 6,
@@ -133,7 +134,7 @@ static const struct {
PhVec2 pos;
int amount;
} ph_item_drops[] = {
- { PH_ITEM_TALENT_SHARD, { 104.0f, 80.0f }, 1 },
+ { PH_ITEM_RUSTED_SPEAR, { 104.0f, 80.0f }, 1 },
};
#if PH_USE_SDL
@@ -148,6 +149,7 @@ static const struct {
#define PH_KEY_INTERACT_1 SDL_SCANCODE_E
#define PH_KEY_INTERACT_2 SDL_SCANCODE_SPACE
#define PH_KEY_CHARACTER SDL_SCANCODE_C
+#define PH_KEY_INVENTORY SDL_SCANCODE_I
#endif
#endif
diff --git a/phantasia.6 b/phantasia.6
@@ -1,4 +1,4 @@
-.Dd April 4, 2026
+.Dd July 29, 2026
.Dt PHANTASIA 6
.Os
.Sh NAME
@@ -20,6 +20,8 @@ Move the player.
Interact and pick up nearby loot.
.It C
Open or close the character sheet.
+.It I
+Open or close the inventory.
.It Window close button
Quit the game.
.El
diff --git a/src/game/main.c b/src/game/main.c
@@ -16,6 +16,12 @@
typedef struct {
SDL_Texture *textures[LEN(ph_asset_defs)];
} PhAssets;
+
+typedef enum {
+ PH_MENU_NONE,
+ PH_MENU_CHARACTER,
+ PH_MENU_INVENTORY,
+} PhMenu;
#endif
#if PH_USE_SDL
@@ -350,6 +356,25 @@ ph_format_bonuses(char *text, size_t size, PhStats bonuses)
}
static void
+ph_draw_menu(SDL_Renderer *renderer, const char *title, SDL_Scancode key)
+{
+ SDL_FRect panel = { 12.0f, 12.0f, PH_VIEW_W - 24.0f, PH_VIEW_H - 24.0f };
+ char text[32];
+ float x;
+
+ SDL_SetRenderDrawColor(renderer, PH_MENU_COLOR);
+ SDL_RenderFillRect(renderer, &panel);
+ SDL_SetRenderDrawColor(renderer, PH_MENU_BORDER_COLOR);
+ SDL_RenderRect(renderer, &panel);
+ SDL_SetRenderDrawColor(renderer, PH_MENU_TEXT_COLOR);
+ SDL_RenderDebugText(renderer, 24.0f, 22.0f, title);
+ snprintf(text, sizeof(text), "CLOSE: %s", SDL_GetScancodeName(key));
+ x = (float)(PH_VIEW_W - 20) - (float)strlen(text) * 8.0f;
+ if (x < 24.0f) x = 24.0f;
+ SDL_RenderDebugText(renderer, x, (float)(PH_VIEW_H - 22), text);
+}
+
+static void
ph_draw_character_sheet(SDL_Renderer *renderer, const PhAssets *assets,
const PhWorld *world)
{
@@ -361,19 +386,13 @@ ph_draw_character_sheet(SDL_Renderer *renderer, const PhAssets *assets,
const PhEntity *player = ph_world_player(world);
const PhEntityDef *def;
PhStats stats;
- SDL_FRect panel = { 12.0f, 12.0f, PH_VIEW_W - 24.0f, PH_VIEW_H - 24.0f };
SDL_FRect avatar = { 32.0f, 46.0f, 48.0f, 48.0f };
char text[128];
int slot;
if (!player || !(def = ph_world_entity_def(world, player->type_id))) return;
stats = ph_world_entity_stats(world, player);
- SDL_SetRenderDrawColor(renderer, PH_SHEET_COLOR);
- SDL_RenderFillRect(renderer, &panel);
- SDL_SetRenderDrawColor(renderer, PH_SHEET_BORDER_COLOR);
- SDL_RenderRect(renderer, &panel);
- SDL_SetRenderDrawColor(renderer, PH_SHEET_TEXT_COLOR);
- SDL_RenderDebugText(renderer, 24.0f, 22.0f, "CHARACTER");
+ ph_draw_menu(renderer, "CHARACTER", PH_KEY_CHARACTER);
SDL_RenderDebugText(renderer, 104.0f, 38.0f, def->name);
if (def->asset_id > PH_ASSET_NONE && (size_t)def->asset_id < LEN(assets->textures))
@@ -396,7 +415,7 @@ ph_draw_character_sheet(SDL_Renderer *renderer, const PhAssets *assets,
float y = 130.0f + (float)(slot - PH_EQUIP_WEAPON) * 27.0f;
snprintf(text, sizeof(text), "%s %s", slot_names[slot], item ? item->name : "-");
- SDL_SetRenderDrawColor(renderer, PH_SHEET_TEXT_COLOR);
+ SDL_SetRenderDrawColor(renderer, PH_MENU_TEXT_COLOR);
SDL_RenderDebugText(renderer, 48.0f, y, text);
if (!item) continue;
if (item->asset_id > PH_ASSET_NONE &&
@@ -406,27 +425,68 @@ ph_draw_character_sheet(SDL_Renderer *renderer, const PhAssets *assets,
item->sprite_tile_x, item->sprite_tile_y, icon, SDL_FLIP_NONE);
}
ph_format_bonuses(text, sizeof(text), item->bonuses);
- SDL_SetRenderDrawColor(renderer, PH_SHEET_BONUS_COLOR);
+ SDL_SetRenderDrawColor(renderer, PH_MENU_BONUS_COLOR);
SDL_RenderDebugText(renderer, 48.0f, y + 10.0f, text);
}
- SDL_SetRenderDrawColor(renderer, PH_SHEET_TEXT_COLOR);
- snprintf(text, sizeof(text), "CLOSE: %s", SDL_GetScancodeName(PH_KEY_CHARACTER));
- SDL_RenderDebugText(renderer, (float)(PH_VIEW_W - 88), (float)(PH_VIEW_H - 22), text);
+}
+
+static void
+ph_draw_inventory(SDL_Renderer *renderer, const PhAssets *assets, const PhWorld *world)
+{
+ char text[128];
+ int shown = 0;
+ int more = 0;
+ int i;
+
+ ph_draw_menu(renderer, "INVENTORY", PH_KEY_INVENTORY);
+ for (i = 0; i < world->item_def_count; ++i) {
+ const PhItemDef *item = &world->item_defs[i];
+ int amount = world->inventory[i];
+ float y;
+
+ if (amount <= 0) continue;
+ if (shown >= 6) {
+ ++more;
+ continue;
+ }
+ y = 48.0f + (float)shown++ * 27.0f;
+ if (item->asset_id > PH_ASSET_NONE &&
+ (size_t)item->asset_id < LEN(assets->textures)) {
+ SDL_FRect icon = { 24.0f, y - 4.0f, 16.0f, 16.0f };
+ ph_draw_sprite(renderer, assets->textures[item->asset_id],
+ item->sprite_tile_x, item->sprite_tile_y, icon, SDL_FLIP_NONE);
+ }
+ SDL_SetRenderDrawColor(renderer, PH_MENU_TEXT_COLOR);
+ snprintf(text, sizeof(text), "%s x%d", item->name ? item->name : "Unknown", amount);
+ SDL_RenderDebugText(renderer, 48.0f, y, text);
+ ph_format_bonuses(text, sizeof(text), item->bonuses);
+ if (!text[0]) snprintf(text, sizeof(text), "VALUE %d", item->value);
+ SDL_SetRenderDrawColor(renderer, PH_MENU_BONUS_COLOR);
+ SDL_RenderDebugText(renderer, 48.0f, y + 10.0f, text);
+ }
+ SDL_SetRenderDrawColor(renderer, PH_MENU_TEXT_COLOR);
+ if (!shown) SDL_RenderDebugText(renderer, 24.0f, 48.0f, "EMPTY");
+ if (more) {
+ snprintf(text, sizeof(text), "+%d MORE", more);
+ SDL_RenderDebugText(renderer, 24.0f, 208.0f, text);
+ }
}
static void
ph_render_frame(SDL_Renderer *renderer, const PhAssets *assets, PhWorld *world,
- PhInput input, float dt, int show_character_sheet)
+ PhInput input, float dt, PhMenu menu)
{
- if (!show_character_sheet) ph_world_tick(world, input, dt);
+ if (menu == PH_MENU_NONE) ph_world_tick(world, input, dt);
SDL_SetRenderDrawColor(renderer, PH_CLEAR_COLOR);
SDL_RenderClear(renderer);
ph_draw_area(renderer, assets, world);
ph_draw_items(renderer, assets, world);
ph_draw_entities(renderer, assets, world);
- if (show_character_sheet) {
+ if (menu == PH_MENU_CHARACTER) {
ph_draw_character_sheet(renderer, assets, world);
+ } else if (menu == PH_MENU_INVENTORY) {
+ ph_draw_inventory(renderer, assets, world);
} else if (world->notice_seconds > 0.0f && world->notice[0] != '\0') {
SDL_FRect notice_bg = {
.x = 8.0f,
@@ -539,9 +599,16 @@ ph_run_render_smoke_test(void)
return 1;
}
+ ph_world_tick(&world, (PhInput){ .move_x = 1 }, 0.1f);
+ ph_world_tick(&world, (PhInput){ .interact = 1 }, 0.0f);
+ if (ph_world_item_amount(&world, PH_ITEM_RUSTED_SPEAR) != 1) {
+ fprintf(stderr, "render smoke test failed: spear pickup\n");
+ result = 1;
+ }
for (i = 0; i < 8; ++i) {
ph_render_frame(renderer, &assets, &world, (PhInput){ 0 },
- 1.0f / 60.0f, i == 7);
+ 1.0f / 60.0f, i == 6 ? PH_MENU_CHARACTER :
+ i == 7 ? PH_MENU_INVENTORY : PH_MENU_NONE);
}
if (!SDL_SaveBMP(surface, PH_RENDER_SMOKE_PATH)) {
@@ -567,7 +634,7 @@ ph_run_game(int frame_limit)
Uint64 last_ticks;
int frame_count = 0;
int running = 1;
- int show_character_sheet = 0;
+ PhMenu menu = PH_MENU_NONE;
if (!SDL_Init(SDL_INIT_VIDEO)) {
fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
@@ -610,7 +677,10 @@ ph_run_game(int frame_limit)
running = 0;
} else if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
event.key.scancode == PH_KEY_CHARACTER) {
- show_character_sheet = !show_character_sheet;
+ menu = menu == PH_MENU_CHARACTER ? PH_MENU_NONE : PH_MENU_CHARACTER;
+ } else if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
+ event.key.scancode == PH_KEY_INVENTORY) {
+ menu = menu == PH_MENU_INVENTORY ? PH_MENU_NONE : PH_MENU_INVENTORY;
}
}
@@ -622,7 +692,7 @@ ph_run_game(int frame_limit)
last_ticks = now_ticks;
input = ph_read_input(SDL_GetKeyboardState(NULL));
- ph_render_frame(renderer, &assets, &world, input, dt, show_character_sheet);
+ ph_render_frame(renderer, &assets, &world, input, dt, menu);
++frame_count;
if (frame_limit > 0 && frame_count >= frame_limit) {
running = 0;