1
0
Fork 0
forked from NotAShelf/rogged

render: use tileset atlas for all entity and tile rendering; anims

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Idb42cff72368e26d8d44db79ba9c413a6a6a6964
This commit is contained in:
raf 2026-04-28 15:32:56 +03:00
commit 5b640dcefd
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
7 changed files with 556 additions and 90 deletions

View file

@ -4,11 +4,14 @@
#include "enemy.h"
#include "items.h"
#include "map/map.h"
#include "map/utils.h"
#include "movement.h"
#include "player.h"
#include "render.h"
#include "rng/rng.h"
#include "settings.h"
#include "tileset/tileset.h"
#include "tileset/tileset_paint.h"
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
@ -182,19 +185,18 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
if (gs->game_over)
return;
// Check if stepped on stairs
if (gs->map.tiles[gs->player.position.y][gs->player.position.x] == TILE_STAIRS) {
gs->awaiting_descend = 1;
gs->last_message = "Descend to next floor? (Y/N)";
gs->message_timer = 120;
return;
}
// combat feedback - player attacked an enemy this turn
if (attacked_enemy != NULL) {
int ex = attacked_enemy->position.x * TILE_SIZE + 8;
int ey = attacked_enemy->position.y * TILE_SIZE;
// Trigger slash effect
gs->slash_timer = 8;
gs->slash_x = attacked_enemy->position.x;
gs->slash_y = attacked_enemy->position.y;
// Use player's equipped weapon damage class, or default to slash
gs->slash_dmg_class = gs->player.has_weapon ? gs->player.equipped_weapon.dmg_class : DMG_SLASH;
if (combat_was_dodged()) {
spawn_floating_label(gs, ex, ey, LABEL_DODGE, EFFECT_NONE);
audio_play_dodge(gs);
@ -237,6 +239,7 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
gs->screen_shake = SHAKE_PLAYER_DAMAGE_DURATION;
gs->damage_taken += combat_get_last_damage();
gs->times_hit++;
gs->player.flash_timer = 4; // Trigger damage flash
spawn_floating_text(gs, gs->player.position.x * TILE_SIZE + 8, gs->player.position.y * TILE_SIZE,
combat_get_last_damage(), combat_was_critical());
}
@ -247,6 +250,13 @@ static void post_action(GameState *gs, Enemy *attacked_enemy) {
if (gs->player.hp <= 0)
gs->game_over = 1;
// Check if stepped on stairs AFTER enemy turns
if (gs->map.tiles[gs->player.position.y][gs->player.position.x] == TILE_STAIRS) {
gs->awaiting_descend = 1;
gs->last_message = "Descend to next floor? (Y/N)";
gs->message_timer = 120;
}
}
// If player is stunned, wait for any key then consume the turn
@ -457,11 +467,27 @@ static int handle_movement_input(GameState *gs) {
try_move_entity(&gs->player.position, direction, &gs->map, &gs->player, gs->enemies, gs->enemy_count, true);
if (result == MOVE_RESULT_MOVED) {
player_on_move(&gs->player);
// Set walk animation
gs->player.anim_state = PLAYER_ANIM_WALK;
gs->player.anim_frame = 0;
gs->player.anim_timer = 8; // frames to show each walk frame
// Update facing direction
if (direction.x != 0)
gs->player.facing_right = (direction.x > 0);
action = 1;
} else if (result == MOVE_RESULT_BLOCKED_ENEMY) {
target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y);
if (target != NULL) {
player_attack(&gs->player, target);
// Set attack animation
gs->player.anim_state = PLAYER_ANIM_ATTACK;
gs->player.anim_frame = 0;
gs->player.anim_timer = 12; // frames to show attack
// Face the enemy
if (target->position.x > gs->player.position.x)
gs->player.facing_right = 1;
else if (target->position.x < gs->player.position.x)
gs->player.facing_right = 0;
action = 1;
}
}
@ -526,13 +552,35 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
load_audio_assets(&gs);
// font
init_fonts(fm);
// Initialize tileset atlas
if (!tileset_init(&gs.tileset, TILE_SIZE, TILE_SIZE)) {
fprintf(stderr, "Failed to initialize tileset\n");
destroy_fonts(fm);
return;
}
if (!tileset_paint_all(&gs.tileset)) {
fprintf(stderr, "Failed to paint tiles\n");
tileset_destroy(&gs.tileset);
destroy_fonts(fm);
return;
}
if (!tileset_finalize(&gs.tileset)) {
fprintf(stderr, "Failed to finalize tileset\n");
tileset_destroy(&gs.tileset);
destroy_fonts(fm);
return;
}
// Initialize first floor
init_floor(&gs, 1);
// Disable esc to exit
SetExitKey(0);
int frame_counter = 0;
while (!WindowShouldClose()) {
frame_counter++;
// Handle input
if (!gs.game_over) {
// Tick status effects at the start of each frame where input is checked
@ -552,6 +600,12 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
gs.game_won = 0;
load_audio_assets(&gs);
init_fonts(fm);
// Re-initialize tileset for new run
if (!tileset_init(&gs.tileset, TILE_SIZE, TILE_SIZE) || !tileset_paint_all(&gs.tileset) ||
!tileset_finalize(&gs.tileset)) {
fprintf(stderr, "Failed to re-initialize tileset\n");
break;
}
init_floor(&gs, 1);
// Update window title with new seed
char title[128];
@ -567,6 +621,27 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
// Update effects
update_effects(&gs);
// Update slash effect timer
if (gs.slash_timer > 0)
gs.slash_timer--;
// Update player animation
if (gs.player.anim_timer > 0) {
gs.player.anim_timer--;
if (gs.player.anim_timer <= 0) {
// Animation finished, return to idle
gs.player.anim_state = PLAYER_ANIM_IDLE;
gs.player.anim_frame = 0;
} else if (gs.player.anim_state == PLAYER_ANIM_WALK) {
// Toggle walk frame every 4 frames
gs.player.anim_frame = (gs.player.anim_timer / 4) % 2;
}
}
// Update player damage flash
if (gs.player.flash_timer > 0)
gs.player.flash_timer--;
// Render
BeginDrawing();
ClearBackground(BLACK);
@ -576,15 +651,19 @@ static void game_loop(unsigned int run_seed, FontManager *fm) {
cam.zoom = 1.0f;
cam.offset = (Vector2){(float)gs.shake_x, (float)gs.shake_y};
BeginMode2D(cam);
render_map(&gs.map);
render_items(gs.items, gs.item_count, gs.map.visible);
render_enemies(gs.enemies, gs.enemy_count, gs.map.visible);
render_player(&gs.player);
render_map(&gs.map, &gs.tileset);
render_items(gs.items, gs.item_count, gs.map.visible, &gs.tileset);
render_enemies(gs.enemies, gs.enemy_count, gs.map.visible, &gs.tileset, frame_counter);
render_player(&gs.player, &gs.tileset, frame_counter);
// Draw slash effect on top of entities
if (gs.slash_timer > 0) {
render_slash_effect(gs.slash_x, gs.slash_y, gs.slash_dmg_class, gs.slash_timer);
}
EndMode2D();
// Floating texts follow world shake
render_floating_texts(gs.floating_texts, gs.floating_count, gs.shake_x, gs.shake_y);
render_ui(&gs.player, fm);
render_floating_texts(gs.floating_texts, gs.floating_count, gs.shake_x, gs.shake_y, fm);
render_ui(&gs.player, &gs.tileset, fm);
// Draw action log
render_action_log(gs.action_log, gs.log_count, gs.log_head, fm);