phantasia

Phantasia - 2D SDL3 RPG prototype.
git clone git://git.beep.wimdupont.com/phantasia.git
Log | Files | Refs | README | LICENSE

commit ea6ee34572d0f5103be1022f0ae4b6f3b27fe158
parent d928191f038dc512524523aab1d407d3871e198b
Author: beep <beep@wimdupont.com>
Date:   Sat,  4 Apr 2026 17:07:17 +0000

Add slime sprite sheet and controls man page

Diffstat:
MMakefile | 10++++++++--
MREADME.adoc | 7+++++++
Aassets/vendor/16x16-animated-critters/16x16babies.png | 0
Aassets/vendor/16x16-animated-critters/SOURCE.txt | 4++++
Aphantasia.6 | 31+++++++++++++++++++++++++++++++
Msrc/game/main.c | 21+++++++++++++++------
6 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile @@ -10,6 +10,8 @@ HERO_SRC = assets/vendor/base-character-16x16/Hero.png HERO_BMP = $(OBJDIR)/assets/hero.bmp ITEMS_SRC = assets/vendor/16x16-rpg-items/items.png ITEMS_BMP = $(OBJDIR)/assets/items.bmp +CRITTERS_SRC = assets/vendor/16x16-animated-critters/16x16babies.png +CRITTERS_BMP = $(OBJDIR)/assets/critters.bmp SRC = \ src/engine/world.c \ @@ -37,9 +39,9 @@ endif .PHONY: all clean render-smoke smoke -all: $(BIN) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) +all: $(BIN) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) -$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) +$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP) $(CRITTERS_BMP) @mkdir -p $(dir $@) $(CC) $(OBJ) -o $@ $(LDLIBS) @@ -67,6 +69,10 @@ $(ITEMS_BMP): $(ITEMS_SRC) @mkdir -p $(dir $@) magick $< BMP3:$@ +$(CRITTERS_BMP): $(CRITTERS_SRC) + @mkdir -p $(dir $@) + magick $< -alpha off BMP3:$@ + smoke: $(BIN) $(BIN) --smoke-test diff --git a/README.adoc b/README.adoc @@ -38,6 +38,13 @@ With SDL3 available, run the game normally: ./bin/phantasia ---- +Read the man page for controls: + +[source,sh] +---- +man ./phantasia.6 +---- + == Maps Map source files live in `data/maps/*.txt`. diff --git a/assets/vendor/16x16-animated-critters/16x16babies.png b/assets/vendor/16x16-animated-critters/16x16babies.png Binary files differ. diff --git a/assets/vendor/16x16-animated-critters/SOURCE.txt b/assets/vendor/16x16-animated-critters/SOURCE.txt @@ -0,0 +1,4 @@ +16x16 Animated Critters +Author: patvanmackelberg +License: CC0 +Source: https://opengameart.org/content/16x16-animated-critters diff --git a/phantasia.6 b/phantasia.6 @@ -0,0 +1,31 @@ +.Dd April 4, 2026 +.Dt PHANTASIA 6 +.Os +.Sh NAME +.Nm phantasia +.Nd 2D SDL3 RPG prototype +.Sh SYNOPSIS +.Nm +.Op Fl -smoke-test +.Op Fl -render-smoke-test +.Sh DESCRIPTION +.Nm +is a small top-down RPG prototype with map movement, item pickup, and visible +in-world entities. +.Sh KEYBINDINGS +.Bl -tag -width "Arrow keys / WASD" +.It Arrow keys / WASD +Move the player. +.It Space / E +Interact and pick up nearby loot. +.It Window close button +Quit the game. +.El +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl -smoke-test +Run a headless world-update smoke test. +.It Fl -render-smoke-test +Run a headless software-render smoke test and save a frame dump to +.Pa obj/render-smoke.bmp . +.El diff --git a/src/game/main.c b/src/game/main.c @@ -27,12 +27,14 @@ enum { #define PH_TILESET_PATH "obj/assets/overworld_tileset_grass.bmp" #define PH_HERO_PATH "obj/assets/hero.bmp" #define PH_ITEMS_PATH "obj/assets/items.bmp" +#define PH_CRITTERS_PATH "obj/assets/critters.bmp" #define PH_RENDER_SMOKE_PATH "obj/render-smoke.bmp" enum { PH_TILESET_COLUMNS = 12, PH_HERO_COLUMNS = 8, PH_ITEMS_COLUMNS = 8, + PH_CRITTERS_COLUMNS = 16, PH_TILE_GRASS = 13, PH_TILE_BLOCKED = 61, }; @@ -42,6 +44,7 @@ typedef struct { SDL_Texture *tileset; SDL_Texture *hero; SDL_Texture *items; + SDL_Texture *critters; } PhAssets; #endif @@ -71,8 +74,8 @@ ph_make_world(PhWorld *world) .name = "Mire Slime", .max_hp = 12, .move_speed = 45, - .sprite_tile_x = -1, - .sprite_tile_y = -1, + .sprite_tile_x = 12, + .sprite_tile_y = 0, .kind = PH_ENTITY_MONSTER, }); ph_world_add_item_def(world, (PhItemDef){ @@ -157,11 +160,13 @@ ph_load_assets(PhAssets *assets, SDL_Renderer *renderer) assets->tileset = ph_load_sprite_sheet(renderer, PH_TILESET_PATH, 0, 0, 0); assets->hero = ph_load_sprite_sheet(renderer, PH_HERO_PATH, 0, 0, 0); assets->items = ph_load_sprite_sheet(renderer, PH_ITEMS_PATH, 111, 119, 109); + assets->critters = ph_load_sprite_sheet(renderer, PH_CRITTERS_PATH, 129, 255, 94); - if (!assets->tileset || !assets->hero || !assets->items) { + if (!assets->tileset || !assets->hero || !assets->items || !assets->critters) { SDL_DestroyTexture(assets->tileset); SDL_DestroyTexture(assets->hero); SDL_DestroyTexture(assets->items); + SDL_DestroyTexture(assets->critters); memset(assets, 0, sizeof(*assets)); return -1; } @@ -174,6 +179,7 @@ ph_destroy_assets(PhAssets *assets) SDL_DestroyTexture(assets->tileset); SDL_DestroyTexture(assets->hero); SDL_DestroyTexture(assets->items); + SDL_DestroyTexture(assets->critters); memset(assets, 0, sizeof(*assets)); } @@ -267,13 +273,16 @@ ph_draw_entities(SDL_Renderer *renderer, const PhAssets *assets, const PhWorld * continue; } - if (def->kind == PH_ENTITY_PLAYER && def->sprite_tile_x >= 0 && def->sprite_tile_y >= 0) { + if (def->sprite_tile_x >= 0 && def->sprite_tile_y >= 0 && + (def->kind == PH_ENTITY_PLAYER || def->kind == PH_ENTITY_MONSTER)) { + SDL_Texture *sheet = def->kind == PH_ENTITY_PLAYER ? assets->hero : assets->critters; + int columns = def->kind == PH_ENTITY_PLAYER ? PH_HERO_COLUMNS : PH_CRITTERS_COLUMNS; + rect.x = entity->pos.x - world->camera.pos.x - 8.0f; rect.y = entity->pos.y - world->camera.pos.y - 12.0f; rect.w = 16.0f; rect.h = 16.0f; - ph_draw_sprite(renderer, assets->hero, PH_HERO_COLUMNS, - def->sprite_tile_x, def->sprite_tile_y, rect); + ph_draw_sprite(renderer, sheet, columns, def->sprite_tile_x, def->sprite_tile_y, rect); } else { rect.x = entity->pos.x - world->camera.pos.x - 7.0f; rect.y = entity->pos.y - world->camera.pos.y - 12.0f;