forked from NotAShelf/rogged
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Iaac0cda778dd541eb34980f3e902ca726a6a6964
241 lines
5.9 KiB
C
241 lines
5.9 KiB
C
#include "player.h"
|
|
#include "combat.h"
|
|
#include "common.h"
|
|
#include "items.h"
|
|
#include "map.h"
|
|
#include "settings.h"
|
|
#include "utils.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
void player_init(Player *p, int x, int y) {
|
|
p->x = x;
|
|
p->y = y;
|
|
p->hp = PLAYER_BASE_HP;
|
|
p->max_hp = PLAYER_BASE_HP;
|
|
p->attack = PLAYER_BASE_ATTACK;
|
|
p->defense = 0;
|
|
p->floor = 1;
|
|
p->step_count = 0;
|
|
p->speed = 100;
|
|
p->cooldown = 0;
|
|
p->dodge = PLAYER_BASE_DODGE;
|
|
p->block = PLAYER_BASE_BLOCK;
|
|
p->has_weapon = 0;
|
|
p->has_armor = 0;
|
|
memset(&p->equipped_weapon, 0, sizeof(Item));
|
|
memset(&p->equipped_armor, 0, sizeof(Item));
|
|
p->equipped_weapon.picked_up = 1;
|
|
p->equipped_armor.picked_up = 1; // mark as invalid
|
|
p->inventory_count = 0;
|
|
p->effect_count = 0;
|
|
memset(p->effects, 0, sizeof(p->effects));
|
|
|
|
// Initialize inventory to empty
|
|
for (int i = 0; i < MAX_INVENTORY; i++) {
|
|
p->inventory[i].picked_up = 1; // mark as invalid
|
|
}
|
|
}
|
|
|
|
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)
|
|
return &enemies[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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))
|
|
return 0;
|
|
|
|
// Check if walkable
|
|
if (!is_floor(map, new_x, new_y))
|
|
return 0;
|
|
|
|
// Move player
|
|
p->x = new_x;
|
|
p->y = new_y;
|
|
p->step_count += 1;
|
|
// Regen suppressed while poisoned, bleeding, or burning
|
|
if (p->step_count % REGEN_STEP_INTERVAL == 0 && p->hp < p->max_hp &&
|
|
!combat_has_effect(p->effects, p->effect_count, EFFECT_POISON) &&
|
|
!combat_has_effect(p->effects, p->effect_count, EFFECT_BLEED) &&
|
|
!combat_has_effect(p->effects, p->effect_count, EFFECT_BURN)) {
|
|
p->hp += 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void player_attack(Player *p, Enemy *e) {
|
|
// Use combat system
|
|
combat_player_attack(p, e);
|
|
}
|
|
|
|
// Get item at player's current position (for pickup)
|
|
Item *get_item_at_floor(Item *items, int count, int x, int y) {
|
|
for (int i = 0; i < count; i++) {
|
|
if (!items[i].picked_up && items[i].x == x && items[i].y == y) {
|
|
return &items[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int player_pickup(Player *p, Item *i) {
|
|
if (p->inventory_count >= MAX_INVENTORY) {
|
|
return 0;
|
|
}
|
|
|
|
if (i->picked_up) {
|
|
return 0;
|
|
}
|
|
|
|
p->inventory[p->inventory_count] = *i;
|
|
p->inventory_count++;
|
|
|
|
i->picked_up = 1;
|
|
return 1;
|
|
}
|
|
|
|
void player_use_item(Player *p, Item *i) {
|
|
if (p == NULL || i == NULL)
|
|
return;
|
|
if (i->picked_up)
|
|
return; // invalid item
|
|
|
|
// Apply item effect (only potions are consumed)
|
|
if (i->type == ITEM_POTION) {
|
|
item_use(p, i);
|
|
}
|
|
// Weapons and armor are equipped, not consumed
|
|
}
|
|
|
|
int player_use_first_item(Player *p) {
|
|
if (p == NULL || p->inventory_count == 0)
|
|
return 0;
|
|
|
|
// Find first valid item in inventory (skip weapons/armor - must equip explicitly)
|
|
for (int i = 0; i < MAX_INVENTORY; i++) {
|
|
if (!p->inventory[i].picked_up) {
|
|
Item *item = &p->inventory[i];
|
|
|
|
if (item->type == ITEM_POTION) {
|
|
// Apply item effect
|
|
item_use(p, item);
|
|
|
|
// Remove from inventory (shift remaining items)
|
|
player_remove_inventory_item(p, i);
|
|
return 1;
|
|
}
|
|
// Weapons/armor can't be used this way - must equip
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Item *player_get_inventory_item(Player *p, int index) {
|
|
if (p == NULL)
|
|
return NULL;
|
|
if (index < 0 || index >= MAX_INVENTORY)
|
|
return NULL;
|
|
if (p->inventory[index].picked_up)
|
|
return NULL; // invalid/empty
|
|
return &p->inventory[index];
|
|
}
|
|
|
|
void player_remove_inventory_item(Player *p, int index) {
|
|
if (p == NULL)
|
|
return;
|
|
if (index < 0 || index >= MAX_INVENTORY)
|
|
return;
|
|
if (p->inventory[index].picked_up)
|
|
return;
|
|
|
|
// Shift remaining items
|
|
for (int j = index; j < MAX_INVENTORY - 1; j++) {
|
|
p->inventory[j] = p->inventory[j + 1];
|
|
}
|
|
p->inventory_count--;
|
|
p->inventory[MAX_INVENTORY - 1].picked_up = 1;
|
|
}
|
|
|
|
int player_equip_item(Player *p, int inv_index) {
|
|
if (p == NULL)
|
|
return 0;
|
|
if (inv_index < 0 || inv_index >= MAX_INVENTORY)
|
|
return 0;
|
|
|
|
Item *item = player_get_inventory_item(p, inv_index);
|
|
if (item == NULL)
|
|
return 0;
|
|
|
|
if (item->type == ITEM_WEAPON) {
|
|
// Unequip current weapon first
|
|
if (p->has_weapon) {
|
|
p->attack -= p->equipped_weapon.power;
|
|
}
|
|
// Equip new weapon
|
|
p->equipped_weapon = *item;
|
|
p->has_weapon = 1;
|
|
p->attack += item->power;
|
|
// Remove from inventory
|
|
player_remove_inventory_item(p, inv_index);
|
|
return 1;
|
|
}
|
|
|
|
if (item->type == ITEM_ARMOR) {
|
|
// Unequip current armor first
|
|
if (p->has_armor) {
|
|
p->defense -= p->equipped_armor.power;
|
|
p->block -= p->equipped_armor.power / 2;
|
|
if (p->block < PLAYER_BASE_BLOCK)
|
|
p->block = PLAYER_BASE_BLOCK;
|
|
}
|
|
// Equip new armor
|
|
p->equipped_armor = *item;
|
|
p->has_armor = 1;
|
|
p->defense += item->power;
|
|
p->block += item->power / 2; // armor grants block bonus
|
|
// Remove from inventory
|
|
player_remove_inventory_item(p, inv_index);
|
|
return 1;
|
|
}
|
|
|
|
return 0; // not equippable (potion)
|
|
}
|
|
|
|
int player_drop_item(Player *p, int inv_index, Item *items, int item_count) {
|
|
if (p == NULL)
|
|
return 0;
|
|
if (inv_index < 0 || inv_index >= MAX_INVENTORY)
|
|
return 0;
|
|
|
|
Item *item = player_get_inventory_item(p, inv_index);
|
|
if (item == NULL)
|
|
return 0;
|
|
|
|
// Find an empty slot in items array to place the dropped item
|
|
for (int i = 0; i < item_count; i++) {
|
|
if (items[i].picked_up) {
|
|
// Place dropped item at this position
|
|
items[i] = *item;
|
|
items[i].x = p->x;
|
|
items[i].y = p->y;
|
|
items[i].picked_up = 0;
|
|
// Remove from inventory
|
|
player_remove_inventory_item(p, inv_index);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0; // no room to drop
|
|
}
|