From ee116ef33f7d5e673fd7a75f78fc613ee26f1d86 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 5 Apr 2026 20:11:37 +0300 Subject: [PATCH] refactor: split `player_move` and decompose `handle_input` Signed-off-by: NotAShelf Change-Id: Iaac0cda778dd541eb34980f3e902ca726a6a6964 --- src/main.c | 479 ++++++++++++++++++++++++++------------------------- src/player.c | 26 +-- src/player.h | 7 +- 3 files changed, 255 insertions(+), 257 deletions(-) diff --git a/src/main.c b/src/main.c index c1e5f1c..651c7f0 100644 --- a/src/main.c +++ b/src/main.c @@ -130,184 +130,201 @@ static void tick_all_effects(GameState *gs) { } } -// Handle player input - returns: 0=continue, 1=acted, -1=quit -static int handle_input(GameState *gs) { - int dx = 0, dy = 0; +static void post_action(GameState *gs) { + gs->turn_count++; - // Check for quit first (always works) - if (IsKeyPressed(KEY_Q)) { - return -1; + // Tick status effects at the start of this turn + tick_all_effects(gs); + if (gs->game_over) + return; + + // Check if stepped on stairs + if (gs->map.tiles[gs->player.y][gs->player.x] == TILE_STAIRS) { + gs->awaiting_descend = 1; + gs->last_message = "Descend to next floor? (Y/N)"; + gs->message_timer = 120; + return; } - // Check for restart (works during game over) - if (IsKeyPressed(KEY_R) && gs->game_over) { - memset(gs, 0, sizeof(GameState)); - init_floor(gs, 1); + // combat feedback - player attacked enemy + if (combat_get_last_damage() > 0 && !combat_was_player_damage()) { + for (int i = 0; i < gs->enemy_count; i++) { + if (!gs->enemies[i].alive) { + spawn_floating_text(gs, gs->enemies[i].x * TILE_SIZE + 8, gs->enemies[i].y * TILE_SIZE, + combat_get_last_damage(), combat_was_critical()); + break; + } + } + } + + // Enemy turns - uses speed/cooldown system + enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); + + // Check if player took damage + if (combat_was_player_damage() && combat_get_last_damage() > 0) { + audio_play_player_damage(); + gs->screen_shake = 8; + spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(), + combat_was_critical()); + } + + // Set message and check game over + gs->last_message = combat_get_last_message(); + gs->message_timer = 60; + + if (gs->player.hp <= 0) + gs->game_over = 1; +} + +// If player is stunned, wait for any key then consume the turn +static int handle_stun_turn(GameState *gs) { + if (!(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_A) || + IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))) + return 0; + gs->turn_count++; + tick_all_effects(gs); + if (gs->game_over) + return 1; + enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); + if (gs->player.hp <= 0) + gs->game_over = 1; + gs->last_message = "You are stunned!"; + gs->message_timer = 60; + add_log(gs, "Stunned! Lost a turn."); + return 1; +} + +static int handle_inventory_input(GameState *gs) { + if (IsKeyPressed(KEY_I) || IsKeyPressed(KEY_ESCAPE)) { + gs->show_inventory = 0; return 0; } - // If player is stunned, wait for any key then consume the turn - if (combat_has_effect(gs->player.effects, gs->player.effect_count, EFFECT_STUN)) { - if (!(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_A) || - IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))) - return 0; - gs->turn_count++; - tick_all_effects(gs); - if (gs->game_over) + if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) { + gs->inv_selected++; + if (gs->inv_selected >= gs->player.inventory_count) + gs->inv_selected = 0; + return 0; + } + if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) { + gs->inv_selected = (gs->inv_selected == 0) ? (gs->player.inventory_count > 0 ? gs->player.inventory_count - 1 : 0) + : gs->inv_selected - 1; + return 0; + } + + if (IsKeyPressed(KEY_ONE)) + gs->inv_selected = 0; + if (IsKeyPressed(KEY_TWO)) + gs->inv_selected = 1; + if (IsKeyPressed(KEY_THREE)) + gs->inv_selected = 2; + if (IsKeyPressed(KEY_FOUR)) + gs->inv_selected = 3; + if (IsKeyPressed(KEY_FIVE)) + gs->inv_selected = 4; + if (IsKeyPressed(KEY_SIX)) + gs->inv_selected = 5; + if (IsKeyPressed(KEY_SEVEN)) + gs->inv_selected = 6; + if (IsKeyPressed(KEY_EIGHT)) + gs->inv_selected = 7; + if (IsKeyPressed(KEY_NINE)) + gs->inv_selected = 8; + if (IsKeyPressed(KEY_ZERO)) + gs->inv_selected = 9; + + // E to equip selected item + if (IsKeyPressed(KEY_E)) { + if (player_equip_item(&gs->player, gs->inv_selected)) { + gs->last_message = "Item equipped!"; + gs->message_timer = 60; + add_log(gs, "Equipped item"); + if (gs->inv_selected >= gs->player.inventory_count && gs->inv_selected > 0) + gs->inv_selected--; return 1; - enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); - if (gs->player.hp <= 0) { + } + gs->last_message = "Cannot equip that!"; + gs->message_timer = 60; + } + + // U or Enter to use selected item + if (IsKeyPressed(KEY_U) || IsKeyPressed(KEY_ENTER)) { + if (gs->player.inventory_count > 0) { + Item *item = player_get_inventory_item(&gs->player, gs->inv_selected); + if (item != NULL) { + if (item->type == ITEM_POTION) { + player_use_item(&gs->player, item); + player_remove_inventory_item(&gs->player, gs->inv_selected); + gs->last_message = "Used potion!"; + gs->message_timer = 60; + add_log(gs, "Used potion"); + gs->show_inventory = 0; + return 1; + } + gs->last_message = "Equip weapons/armor with E!"; + gs->message_timer = 60; + } + } + } + + // D to drop selected item + if (IsKeyPressed(KEY_D)) { + if (gs->player.inventory_count > 0) { + Item *item = player_get_inventory_item(&gs->player, gs->inv_selected); + if (item != NULL) { + char drop_msg[64]; + snprintf(drop_msg, sizeof(drop_msg), "Dropped %s", item_get_name(item)); + if (player_drop_item(&gs->player, gs->inv_selected, gs->items, gs->item_count)) { + add_log(gs, drop_msg); + gs->last_message = "Item dropped!"; + gs->message_timer = 60; + if (gs->inv_selected >= gs->player.inventory_count && gs->inv_selected > 0) + gs->inv_selected--; + return 1; + } + gs->last_message = "Cannot drop!"; + gs->message_timer = 60; + } + } + } + + return 0; +} + +static int handle_descend_input(GameState *gs) { + if (IsKeyPressed(KEY_Y)) { + if (gs->player.floor < NUM_FLOORS) { + audio_play_stairs(); + init_floor(gs, gs->player.floor + 1); + gs->last_message = "Descended to next floor!"; + gs->message_timer = 60; + add_log(gs, "Descended stairs"); + } else { + gs->game_won = 1; gs->game_over = 1; } - gs->last_message = "You are stunned!"; - gs->message_timer = 60; - add_log(gs, "Stunned! Lost a turn."); + gs->awaiting_descend = 0; return 1; } - - if (gs->show_inventory) { - if (IsKeyPressed(KEY_I) || IsKeyPressed(KEY_ESCAPE)) { - gs->show_inventory = 0; - return 0; - } - - if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) { - gs->inv_selected++; - if (gs->inv_selected >= gs->player.inventory_count) { - gs->inv_selected = 0; - } - return 0; - } - if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) { - if (gs->inv_selected == 0) { - gs->inv_selected = (gs->player.inventory_count > 0) ? gs->player.inventory_count - 1 : 0; - } else { - gs->inv_selected--; - } - return 0; - } - - if (IsKeyPressed(KEY_ONE)) - gs->inv_selected = 0; - if (IsKeyPressed(KEY_TWO)) - gs->inv_selected = 1; - if (IsKeyPressed(KEY_THREE)) - gs->inv_selected = 2; - if (IsKeyPressed(KEY_FOUR)) - gs->inv_selected = 3; - if (IsKeyPressed(KEY_FIVE)) - gs->inv_selected = 4; - if (IsKeyPressed(KEY_SIX)) - gs->inv_selected = 5; - if (IsKeyPressed(KEY_SEVEN)) - gs->inv_selected = 6; - if (IsKeyPressed(KEY_EIGHT)) - gs->inv_selected = 7; - if (IsKeyPressed(KEY_NINE)) - gs->inv_selected = 8; - if (IsKeyPressed(KEY_ZERO)) - gs->inv_selected = 9; - - // E to equip selected item - if (IsKeyPressed(KEY_E)) { - if (player_equip_item(&gs->player, gs->inv_selected)) { - gs->last_message = "Item equipped!"; - gs->message_timer = 60; - add_log(gs, "Equipped item"); - if (gs->inv_selected >= gs->player.inventory_count && gs->inv_selected > 0) { - gs->inv_selected--; - } - return 1; - } else { - gs->last_message = "Cannot equip that!"; - gs->message_timer = 60; - } - } - - // U or Enter to use selected item - if (IsKeyPressed(KEY_U) || IsKeyPressed(KEY_ENTER)) { - if (gs->player.inventory_count > 0) { - Item *item = player_get_inventory_item(&gs->player, gs->inv_selected); - if (item != NULL) { - if (item->type == ITEM_POTION) { - player_use_item(&gs->player, item); - player_remove_inventory_item(&gs->player, gs->inv_selected); - gs->last_message = "Used potion!"; - gs->message_timer = 60; - add_log(gs, "Used potion"); - gs->show_inventory = 0; - return 1; - } else { - gs->last_message = "Equip weapons/armor with E!"; - gs->message_timer = 60; - } - } - } - } - - // D to drop selected item - if (IsKeyPressed(KEY_D)) { - if (gs->player.inventory_count > 0) { - Item *item = player_get_inventory_item(&gs->player, gs->inv_selected); - if (item != NULL) { - char drop_msg[64]; - snprintf(drop_msg, sizeof(drop_msg), "Dropped %s", item_get_name(item)); - if (player_drop_item(&gs->player, gs->inv_selected, gs->items, gs->item_count)) { - add_log(gs, drop_msg); - gs->last_message = "Item dropped!"; - gs->message_timer = 60; - if (gs->inv_selected >= gs->player.inventory_count && gs->inv_selected > 0) { - gs->inv_selected--; - } - return 1; - } else { - gs->last_message = "Cannot drop!"; - gs->message_timer = 60; - } - } - } - } - - return 0; - } - - // Handle descend confirmation - if (gs->awaiting_descend) { - if (IsKeyPressed(KEY_Y)) { - // Descend - if (gs->player.floor < NUM_FLOORS) { - audio_play_stairs(); - init_floor(gs, gs->player.floor + 1); - gs->last_message = "Descended to next floor!"; - gs->message_timer = 60; - add_log(gs, "Descended stairs"); - gs->awaiting_descend = 0; - return 1; - } else { - gs->game_won = 1; - gs->game_over = 1; - gs->awaiting_descend = 0; - return 1; - } - } - if (IsKeyPressed(KEY_N)) { - gs->awaiting_descend = 0; - gs->last_message = "Stayed on floor."; - gs->message_timer = 60; - return 1; - } - return 0; + if (IsKeyPressed(KEY_N)) { + gs->awaiting_descend = 0; + gs->last_message = "Stayed on floor."; + gs->message_timer = 60; + return 1; } + return 0; +} +static int handle_movement_input(GameState *gs) { // Check for inventory toggle (I key) - if (IsKeyPressed(KEY_I) && !gs->game_over) { + if (IsKeyPressed(KEY_I)) { gs->show_inventory = 1; gs->inv_selected = 0; - return 0; // don't consume turn + return 0; } // Check for manual item pickup (G key) - if (IsKeyPressed(KEY_G) && !gs->game_over) { + if (IsKeyPressed(KEY_G)) { Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.x, gs->player.y); if (item != NULL) { if (player_pickup(&gs->player, item)) { @@ -317,97 +334,83 @@ static int handle_input(GameState *gs) { gs->last_message = "Picked up item!"; gs->message_timer = 60; audio_play_item_pickup(); - return 1; } else { gs->last_message = "Inventory full!"; gs->message_timer = 60; - return 1; } + return 1; } } // Check for item usage (U key - use first potion) - if (IsKeyPressed(KEY_U) && !gs->game_over) { - if (gs->player.inventory_count > 0) { - if (player_use_first_item(&gs->player)) { - gs->last_message = "Used potion!"; - gs->message_timer = 60; - audio_play_item_pickup(); - return 1; // consume a turn - } - } - } - - // Movement, use iskeydown for held key repeat, with delay - if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) { - dy = -1; - } else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) { - dy = 1; - } else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) { - dx = -1; - } else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) { - dx = 1; - } - - if (dx != 0 || dy != 0) { - // Reset combat message - combat_reset_event(); - - // Player action - int action = player_move(&gs->player, dx, dy, &gs->map, gs->enemies, gs->enemy_count); - - if (action) { - // Increment turn counter - gs->turn_count++; - - // Tick status effects at the start of this turn - tick_all_effects(gs); - if (gs->game_over) - return 1; - - // Check if stepped on stairs - if (gs->map.tiles[gs->player.y][gs->player.x] == TILE_STAIRS) { - gs->awaiting_descend = 1; - gs->last_message = "Descend to next floor? (Y/N)"; - gs->message_timer = 120; - return 1; - } - - // combat feedback - player attacked enemy - if (combat_get_last_damage() > 0 && !combat_was_player_damage()) { - // find the enemy we attacked - for (int i = 0; i < gs->enemy_count; i++) { - if (!gs->enemies[i].alive && combat_get_last_damage() > 0) { - spawn_floating_text(gs, gs->enemies[i].x * TILE_SIZE + 8, gs->enemies[i].y * TILE_SIZE, - combat_get_last_damage(), combat_was_critical()); - break; - } - } - } - - // Enemy turns - now uses speed/cooldown system (no more % 2 hack) - enemy_update_all(gs->enemies, gs->enemy_count, &gs->player, &gs->map); - - // Check if player took damage - if (combat_was_player_damage() && combat_get_last_damage() > 0) { - audio_play_player_damage(); - gs->screen_shake = 8; - spawn_floating_text(gs, gs->player.x * TILE_SIZE + 8, gs->player.y * TILE_SIZE, combat_get_last_damage(), - combat_was_critical()); - } - - // Set message - gs->last_message = combat_get_last_message(); + if (IsKeyPressed(KEY_U)) { + if (gs->player.inventory_count > 0 && player_use_first_item(&gs->player)) { + gs->last_message = "Used potion!"; gs->message_timer = 60; - - // Check game over - if (gs->player.hp <= 0) { - gs->game_over = 1; - } + audio_play_item_pickup(); + return 1; } } - return 0; + // Movement: use IsKeyDown for held-key repeat + int dx = 0, dy = 0; + if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) + dy = -1; + else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) + dy = 1; + else if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) + dx = -1; + else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) + dx = 1; + + if (dx == 0 && dy == 0) + return 0; + + // Reset combat event before player acts + combat_reset_event(); + + int new_x = gs->player.x + dx; + int new_y = gs->player.y + dy; + int action = 0; + + // Attack enemy at target tile, or move into it + Enemy *target = player_find_enemy_at(gs->enemies, gs->enemy_count, new_x, new_y); + if (target != NULL) { + player_attack(&gs->player, target); + action = 1; + } else { + action = player_move(&gs->player, dx, dy, &gs->map); + } + + if (action) + post_action(gs); + + return action; +} + +// Handle player input - returns: 0=continue, 1=acted, -1=quit +static int handle_input(GameState *gs) { + // Check for quit first (always works) + if (IsKeyPressed(KEY_Q)) + return -1; + + // Check for restart (works during game over) + if (IsKeyPressed(KEY_R) && gs->game_over) { + memset(gs, 0, sizeof(GameState)); + init_floor(gs, 1); + return 0; + } + + if (combat_has_effect(gs->player.effects, gs->player.effect_count, EFFECT_STUN)) + return handle_stun_turn(gs); + + if (gs->show_inventory) + return handle_inventory_input(gs); + + if (gs->awaiting_descend) + return handle_descend_input(gs); + + return handle_movement_input(gs); } // Main game loop diff --git a/src/player.c b/src/player.c index 9dbadf1..3bb4c4d 100644 --- a/src/player.c +++ b/src/player.c @@ -37,37 +37,29 @@ void player_init(Player *p, int x, int y) { } } -// Check if position has an enemy -static Enemy *get_enemy_at(Enemy *enemies, int count, int x, int y) { +Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y) { + if (enemies == NULL || count <= 0) + return NULL; + if (count > MAX_ENEMIES) + count = MAX_ENEMIES; for (int i = 0; i < count; i++) { - if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y) { + if (enemies[i].alive && enemies[i].x == x && enemies[i].y == y) return &enemies[i]; - } } return NULL; } -int player_move(Player *p, int dx, int dy, Map *map, Enemy *enemies, int enemy_count) { +int player_move(Player *p, int dx, int dy, Map *map) { int new_x = p->x + dx; int new_y = p->y + dy; // Check bounds - if (!in_bounds(new_x, new_y, MAP_WIDTH, MAP_HEIGHT)) { + if (!in_bounds(new_x, new_y, MAP_WIDTH, MAP_HEIGHT)) return 0; - } // Check if walkable - if (!is_floor(map, new_x, new_y)) { + if (!is_floor(map, new_x, new_y)) return 0; - } - - // Check for enemy at target position - Enemy *enemy = get_enemy_at(enemies, enemy_count, new_x, new_y); - if (enemy != NULL) { - // Attack the enemy - player_attack(p, enemy); - return 1; - } // Move player p->x = new_x; diff --git a/src/player.h b/src/player.h index 7d6c151..ca91abf 100644 --- a/src/player.h +++ b/src/player.h @@ -6,8 +6,11 @@ // Initialize player at position void player_init(Player *p, int x, int y); -// Move player, return 1 if moved/attacked, 0 if blocked -int player_move(Player *p, int dx, int dy, Map *map, Enemy *enemies, int enemy_count); +// Move player to (x+dx, y+dy); returns 1 if moved, 0 if blocked +int player_move(Player *p, int dx, int dy, Map *map); + +// Find a living enemy at tile (x, y); returns NULL if none +Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y); // Player attacks enemy (deal damage) void player_attack(Player *p, Enemy *e);