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:
parent
2f5c959500
commit
5b640dcefd
7 changed files with 556 additions and 90 deletions
107
src/main.c
107
src/main.c
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue