commit d928191f038dc512524523aab1d407d3871e198b
parent 42592a9842d3a8d0e4646424abb3bf1a3a0dc9b4
Author: beep <beep@wimdupont.com>
Date: Sat, 4 Apr 2026 17:04:31 +0000
Add CC0 hero and item sprites
Diffstat:
7 files changed, 132 insertions(+), 38 deletions(-)
diff --git a/Makefile b/Makefile
@@ -6,6 +6,10 @@ MAPSRC = data/maps/ashen-meadow.txt
MAPBIN = $(MAPSRC:data/maps/%.txt=$(MAPDIR)/%.phmap)
TILESET_SRC = assets/vendor/overworld-grass-biome/TilesetGrass/overworld_tileset_grass.png
TILESET_BMP = $(OBJDIR)/assets/overworld_tileset_grass.bmp
+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
SRC = \
src/engine/world.c \
@@ -33,9 +37,9 @@ endif
.PHONY: all clean render-smoke smoke
-all: $(BIN) $(MAPBIN) $(TILESET_BMP)
+all: $(BIN) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP)
-$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP)
+$(BIN): $(OBJ) $(MAPBIN) $(TILESET_BMP) $(HERO_BMP) $(ITEMS_BMP)
@mkdir -p $(dir $@)
$(CC) $(OBJ) -o $@ $(LDLIBS)
@@ -55,6 +59,14 @@ $(TILESET_BMP): $(TILESET_SRC)
@mkdir -p $(dir $@)
magick $< -alpha off BMP3:$@
+$(HERO_BMP): $(HERO_SRC)
+ @mkdir -p $(dir $@)
+ magick $< -alpha off BMP3:$@
+
+$(ITEMS_BMP): $(ITEMS_SRC)
+ @mkdir -p $(dir $@)
+ magick $< BMP3:$@
+
smoke: $(BIN)
$(BIN) --smoke-test
diff --git a/assets/vendor/16x16-rpg-items/SOURCE.txt b/assets/vendor/16x16-rpg-items/SOURCE.txt
@@ -0,0 +1,4 @@
+16x16 RPG Items
+Author: Jetrel
+License: CC0
+Source: https://opengameart.org/content/16x16-rpg-items
diff --git a/assets/vendor/16x16-rpg-items/items.png b/assets/vendor/16x16-rpg-items/items.png
Binary files differ.
diff --git a/assets/vendor/base-character-16x16/Hero.png b/assets/vendor/base-character-16x16/Hero.png
Binary files differ.
diff --git a/assets/vendor/base-character-16x16/SOURCE.txt b/assets/vendor/base-character-16x16/SOURCE.txt
@@ -0,0 +1,4 @@
+Base Character Spritesheet 16x16
+Author: Cough-E
+License: CC0
+Source: https://opengameart.org/content/base-character-spritesheet-16x16
diff --git a/src/engine/world.h b/src/engine/world.h
@@ -38,6 +38,8 @@ typedef struct {
int value;
int power;
int talent_points;
+ int sprite_tile_x;
+ int sprite_tile_y;
} PhItemDef;
typedef struct {
diff --git a/src/game/main.c b/src/game/main.c
@@ -25,14 +25,26 @@ enum {
#define PH_START_MAP_PATH "obj/maps/ashen-meadow.phmap"
#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_RENDER_SMOKE_PATH "obj/render-smoke.bmp"
enum {
PH_TILESET_COLUMNS = 12,
+ PH_HERO_COLUMNS = 8,
+ PH_ITEMS_COLUMNS = 8,
PH_TILE_GRASS = 13,
PH_TILE_BLOCKED = 61,
};
+#if PH_USE_SDL
+typedef struct {
+ SDL_Texture *tileset;
+ SDL_Texture *hero;
+ SDL_Texture *items;
+} PhAssets;
+#endif
+
static int
ph_make_world(PhWorld *world)
{
@@ -51,7 +63,7 @@ ph_make_world(PhWorld *world)
.max_hp = 40,
.move_speed = 90,
.sprite_tile_x = 0,
- .sprite_tile_y = 0,
+ .sprite_tile_y = 2,
.kind = PH_ENTITY_PLAYER,
});
ph_world_add_entity_def(world, (PhEntityDef){
@@ -59,8 +71,8 @@ ph_make_world(PhWorld *world)
.name = "Mire Slime",
.max_hp = 12,
.move_speed = 45,
- .sprite_tile_x = 1,
- .sprite_tile_y = 0,
+ .sprite_tile_x = -1,
+ .sprite_tile_y = -1,
.kind = PH_ENTITY_MONSTER,
});
ph_world_add_item_def(world, (PhItemDef){
@@ -69,6 +81,8 @@ ph_make_world(PhWorld *world)
.value = 30,
.power = 0,
.talent_points = 1,
+ .sprite_tile_x = 2,
+ .sprite_tile_y = 6,
});
player = ph_world_spawn_entity(world, PH_DEF_PLAYER, (PhVec2){ 80.0f, 80.0f });
@@ -115,7 +129,7 @@ ph_run_smoke_test(void)
#if PH_USE_SDL
static SDL_Texture *
-ph_load_tileset(SDL_Renderer *renderer, const char *path)
+ph_load_sprite_sheet(SDL_Renderer *renderer, const char *path, Uint8 key_r, Uint8 key_g, Uint8 key_b)
{
SDL_Surface *surface;
SDL_Texture *texture;
@@ -126,7 +140,7 @@ ph_load_tileset(SDL_Renderer *renderer, const char *path)
return NULL;
}
- SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, 0, 0, 0));
+ SDL_SetSurfaceColorKey(surface, true, SDL_MapSurfaceRGB(surface, key_r, key_g, key_b));
texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_DestroySurface(surface);
if (!texture) {
@@ -135,8 +149,51 @@ ph_load_tileset(SDL_Renderer *renderer, const char *path)
return texture;
}
+static int
+ph_load_assets(PhAssets *assets, SDL_Renderer *renderer)
+{
+ memset(assets, 0, sizeof(*assets));
+
+ 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);
+
+ if (!assets->tileset || !assets->hero || !assets->items) {
+ SDL_DestroyTexture(assets->tileset);
+ SDL_DestroyTexture(assets->hero);
+ SDL_DestroyTexture(assets->items);
+ memset(assets, 0, sizeof(*assets));
+ return -1;
+ }
+ return 0;
+}
+
static void
-ph_draw_area(SDL_Renderer *renderer, SDL_Texture *tileset, const PhWorld *world)
+ph_destroy_assets(PhAssets *assets)
+{
+ SDL_DestroyTexture(assets->tileset);
+ SDL_DestroyTexture(assets->hero);
+ SDL_DestroyTexture(assets->items);
+ memset(assets, 0, sizeof(*assets));
+}
+
+static void
+ph_draw_sprite(SDL_Renderer *renderer, SDL_Texture *texture, int columns,
+ int sprite_tile_x, int sprite_tile_y, SDL_FRect dst)
+{
+ SDL_FRect src = {
+ .x = (float)(sprite_tile_x * PH_TILE_SIZE),
+ .y = (float)(sprite_tile_y * PH_TILE_SIZE),
+ .w = (float)PH_TILE_SIZE,
+ .h = (float)PH_TILE_SIZE,
+ };
+
+ (void)columns;
+ SDL_RenderTexture(renderer, texture, &src, &dst);
+}
+
+static void
+ph_draw_area(SDL_Renderer *renderer, const PhAssets *assets, const PhWorld *world)
{
int tx;
int ty;
@@ -158,18 +215,19 @@ ph_draw_area(SDL_Renderer *renderer, SDL_Texture *tileset, const PhWorld *world)
.h = (float)PH_TILE_SIZE,
};
- SDL_RenderTexture(renderer, tileset, &src, &rect);
+ SDL_RenderTexture(renderer, assets->tileset, &src, &rect);
}
}
}
static void
-ph_draw_items(SDL_Renderer *renderer, const PhWorld *world)
+ph_draw_items(SDL_Renderer *renderer, const PhAssets *assets, const PhWorld *world)
{
int i;
for (i = 0; i < world->ground_item_count; ++i) {
const PhGroundItem *drop = &world->ground_items[i];
+ const PhItemDef *def = ph_world_item_def(world, drop->item_id);
SDL_FRect rect;
if (!drop->active) {
@@ -181,13 +239,22 @@ ph_draw_items(SDL_Renderer *renderer, const PhWorld *world)
rect.w = 6.0f;
rect.h = 6.0f;
- SDL_SetRenderDrawColor(renderer, 242, 214, 80, 255);
- SDL_RenderFillRect(renderer, &rect);
+ if (def && def->sprite_tile_x >= 0 && def->sprite_tile_y >= 0) {
+ rect.x = drop->pos.x - world->camera.pos.x - 8.0f;
+ rect.y = drop->pos.y - world->camera.pos.y - 8.0f;
+ rect.w = 16.0f;
+ rect.h = 16.0f;
+ ph_draw_sprite(renderer, assets->items, PH_ITEMS_COLUMNS,
+ def->sprite_tile_x, def->sprite_tile_y, rect);
+ } else {
+ SDL_SetRenderDrawColor(renderer, 242, 214, 80, 255);
+ SDL_RenderFillRect(renderer, &rect);
+ }
}
}
static void
-ph_draw_entities(SDL_Renderer *renderer, const PhWorld *world)
+ph_draw_entities(SDL_Renderer *renderer, const PhAssets *assets, const PhWorld *world)
{
int i;
@@ -200,32 +267,39 @@ ph_draw_entities(SDL_Renderer *renderer, const PhWorld *world)
continue;
}
- rect.x = entity->pos.x - world->camera.pos.x - 7.0f;
- rect.y = entity->pos.y - world->camera.pos.y - 12.0f;
- rect.w = 14.0f;
- rect.h = 18.0f;
-
- if (def->kind == PH_ENTITY_PLAYER) {
- SDL_SetRenderDrawColor(renderer, 110, 180, 255, 255);
- } else if (def->kind == PH_ENTITY_MONSTER) {
- SDL_SetRenderDrawColor(renderer, 168, 108, 224, 255);
+ if (def->kind == PH_ENTITY_PLAYER && def->sprite_tile_x >= 0 && def->sprite_tile_y >= 0) {
+ 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);
} else {
- SDL_SetRenderDrawColor(renderer, 220, 180, 90, 255);
+ rect.x = entity->pos.x - world->camera.pos.x - 7.0f;
+ rect.y = entity->pos.y - world->camera.pos.y - 12.0f;
+ rect.w = 14.0f;
+ rect.h = 18.0f;
+
+ if (def->kind == PH_ENTITY_MONSTER) {
+ SDL_SetRenderDrawColor(renderer, 168, 108, 224, 255);
+ } else {
+ SDL_SetRenderDrawColor(renderer, 220, 180, 90, 255);
+ }
+ SDL_RenderFillRect(renderer, &rect);
}
- SDL_RenderFillRect(renderer, &rect);
}
}
static void
-ph_render_frame(SDL_Renderer *renderer, SDL_Texture *tileset, PhWorld *world, PhInput input, float dt)
+ph_render_frame(SDL_Renderer *renderer, const PhAssets *assets, PhWorld *world, PhInput input, float dt)
{
ph_world_tick(world, input, dt);
SDL_SetRenderDrawColor(renderer, 16, 18, 24, 255);
SDL_RenderClear(renderer);
- ph_draw_area(renderer, tileset, world);
- ph_draw_items(renderer, world);
- ph_draw_entities(renderer, world);
+ ph_draw_area(renderer, assets, world);
+ ph_draw_items(renderer, assets, world);
+ ph_draw_entities(renderer, assets, world);
SDL_RenderPresent(renderer);
}
@@ -285,7 +359,7 @@ ph_run_render_smoke_test(void)
{
SDL_Surface *surface;
SDL_Renderer *renderer;
- SDL_Texture *tileset;
+ PhAssets assets;
PhWorld world;
int i;
@@ -316,8 +390,7 @@ ph_run_render_smoke_test(void)
return 1;
}
- tileset = ph_load_tileset(renderer, PH_TILESET_PATH);
- if (!tileset) {
+ if (ph_load_assets(&assets, renderer) < 0) {
ph_area_free(&world.area);
SDL_DestroyRenderer(renderer);
SDL_DestroySurface(surface);
@@ -326,14 +399,14 @@ ph_run_render_smoke_test(void)
}
for (i = 0; i < 8; ++i) {
- ph_render_frame(renderer, tileset, &world, (PhInput){ 0 }, 1.0f / 60.0f);
+ ph_render_frame(renderer, &assets, &world, (PhInput){ 0 }, 1.0f / 60.0f);
}
if (!SDL_SaveBMP(surface, PH_RENDER_SMOKE_PATH)) {
fprintf(stderr, "SDL_SaveBMP failed: %s\n", SDL_GetError());
}
- SDL_DestroyTexture(tileset);
+ ph_destroy_assets(&assets);
ph_area_free(&world.area);
SDL_DestroyRenderer(renderer);
SDL_DestroySurface(surface);
@@ -346,7 +419,7 @@ ph_run_game(int frame_limit)
{
SDL_Window *window;
SDL_Renderer *renderer;
- SDL_Texture *tileset;
+ PhAssets assets;
PhWorld world;
Uint64 last_ticks;
int frame_count = 0;
@@ -368,8 +441,7 @@ ph_run_game(int frame_limit)
return 1;
}
- tileset = ph_load_tileset(renderer, PH_TILESET_PATH);
- if (!tileset) {
+ if (ph_load_assets(&assets, renderer) < 0) {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
ph_area_free(&world.area);
@@ -403,7 +475,7 @@ ph_run_game(int frame_limit)
last_ticks = now_ticks;
input = ph_read_input(SDL_GetKeyboardState(NULL));
- ph_render_frame(renderer, tileset, &world, input, dt);
+ ph_render_frame(renderer, &assets, &world, input, dt);
++frame_count;
if (frame_limit > 0 && frame_count >= frame_limit) {
running = 0;
@@ -411,7 +483,7 @@ ph_run_game(int frame_limit)
SDL_Delay(16);
}
- SDL_DestroyTexture(tileset);
+ ph_destroy_assets(&assets);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
ph_area_free(&world.area);