refactor: split player_move and decompose handle_input

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Iaac0cda778dd541eb34980f3e902ca726a6a6964
This commit is contained in:
raf 2026-04-05 20:11:37 +03:00
commit ee116ef33f
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
3 changed files with 228 additions and 230 deletions

View file

@ -130,184 +130,201 @@ static void tick_all_effects(GameState *gs) {
} }
} }
// Handle player input - returns: 0=continue, 1=acted, -1=quit static void post_action(GameState *gs) {
static int handle_input(GameState *gs) { gs->turn_count++;
int dx = 0, dy = 0;
// Check for quit first (always works) // Tick status effects at the start of this turn
if (IsKeyPressed(KEY_Q)) { tick_all_effects(gs);
return -1; 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) // combat feedback - player attacked enemy
if (IsKeyPressed(KEY_R) && gs->game_over) { if (combat_get_last_damage() > 0 && !combat_was_player_damage()) {
memset(gs, 0, sizeof(GameState)); for (int i = 0; i < gs->enemy_count; i++) {
init_floor(gs, 1); 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; return 0;
} }
// If player is stunned, wait for any key then consume the turn if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) {
if (combat_has_effect(gs->player.effects, gs->player.effect_count, EFFECT_STUN)) { gs->inv_selected++;
if (!(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_A) || if (gs->inv_selected >= gs->player.inventory_count)
IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))) gs->inv_selected = 0;
return 0; return 0;
gs->turn_count++; }
tick_all_effects(gs); if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) {
if (gs->game_over) 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; 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->game_over = 1;
} }
gs->last_message = "You are stunned!"; gs->awaiting_descend = 0;
gs->message_timer = 60;
add_log(gs, "Stunned! Lost a turn.");
return 1; return 1;
} }
if (IsKeyPressed(KEY_N)) {
if (gs->show_inventory) { gs->awaiting_descend = 0;
if (IsKeyPressed(KEY_I) || IsKeyPressed(KEY_ESCAPE)) { gs->last_message = "Stayed on floor.";
gs->show_inventory = 0; gs->message_timer = 60;
return 0; return 1;
}
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;
} }
return 0;
}
static int handle_movement_input(GameState *gs) {
// Check for inventory toggle (I key) // Check for inventory toggle (I key)
if (IsKeyPressed(KEY_I) && !gs->game_over) { if (IsKeyPressed(KEY_I)) {
gs->show_inventory = 1; gs->show_inventory = 1;
gs->inv_selected = 0; gs->inv_selected = 0;
return 0; // don't consume turn return 0;
} }
// Check for manual item pickup (G key) // 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); Item *item = get_item_at_floor(gs->items, gs->item_count, gs->player.x, gs->player.y);
if (item != NULL) { if (item != NULL) {
if (player_pickup(&gs->player, item)) { if (player_pickup(&gs->player, item)) {
@ -317,97 +334,83 @@ static int handle_input(GameState *gs) {
gs->last_message = "Picked up item!"; gs->last_message = "Picked up item!";
gs->message_timer = 60; gs->message_timer = 60;
audio_play_item_pickup(); audio_play_item_pickup();
return 1;
} else { } else {
gs->last_message = "Inventory full!"; gs->last_message = "Inventory full!";
gs->message_timer = 60; gs->message_timer = 60;
return 1;
} }
return 1;
} }
} }
// Check for item usage (U key - use first potion) // Check for item usage (U key - use first potion)
if (IsKeyPressed(KEY_U) && !gs->game_over) { if (IsKeyPressed(KEY_U)) {
if (gs->player.inventory_count > 0) { if (gs->player.inventory_count > 0 && player_use_first_item(&gs->player)) {
if (player_use_first_item(&gs->player)) { gs->last_message = "Used potion!";
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();
gs->message_timer = 60; gs->message_timer = 60;
audio_play_item_pickup();
// Check game over return 1;
if (gs->player.hp <= 0) {
gs->game_over = 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 // Main game loop

View file

@ -37,37 +37,29 @@ void player_init(Player *p, int x, int y) {
} }
} }
// Check if position has an enemy Enemy *player_find_enemy_at(Enemy *enemies, int count, int x, int y) {
static Enemy *get_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++) { 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 &enemies[i];
}
} }
return NULL; 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_x = p->x + dx;
int new_y = p->y + dy; int new_y = p->y + dy;
// Check bounds // 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; return 0;
}
// Check if walkable // Check if walkable
if (!is_floor(map, new_x, new_y)) { if (!is_floor(map, new_x, new_y))
return 0; 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 // Move player
p->x = new_x; p->x = new_x;

View file

@ -6,8 +6,11 @@
// Initialize player at position // Initialize player at position
void player_init(Player *p, int x, int y); void player_init(Player *p, int x, int y);
// Move player, return 1 if moved/attacked, 0 if blocked // 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, Enemy *enemies, int enemy_count); 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) // Player attacks enemy (deal damage)
void player_attack(Player *p, Enemy *e); void player_attack(Player *p, Enemy *e);