forked from NotAShelf/rogged
enemy: add alert memory; vision variance based on type
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I2f5c7cac72c8772e5871b99026d106b46a6a6964
This commit is contained in:
parent
f85d28e932
commit
71343311eb
3 changed files with 90 additions and 3 deletions
|
|
@ -104,6 +104,11 @@ typedef struct {
|
|||
int status_chance;
|
||||
int crit_chance; // crit chance percentage (0-100)
|
||||
int crit_mult; // crit damage multiplier percentage (e.g. 150 = 1.5x)
|
||||
// vision
|
||||
int vision_range;
|
||||
int alert; // 1 = aware of player, searching
|
||||
int last_known_x; // last position where enemy saw player
|
||||
int last_known_y;
|
||||
// status effects
|
||||
StatusEffect effects[MAX_EFFECTS];
|
||||
int effect_count;
|
||||
|
|
|
|||
86
src/enemy.c
86
src/enemy.c
|
|
@ -70,6 +70,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
|
|||
e.resistance[DMG_PIERCE] = 0;
|
||||
e.resistance[DMG_FIRE] = -25;
|
||||
e.resistance[DMG_POISON] = 50;
|
||||
e.vision_range = 7;
|
||||
break;
|
||||
case ENEMY_SKELETON:
|
||||
e.max_hp = ENEMY_BASE_HP + floor + 2;
|
||||
|
|
@ -87,6 +88,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
|
|||
e.resistance[DMG_PIERCE] = 50;
|
||||
e.resistance[DMG_FIRE] = 25;
|
||||
e.resistance[DMG_POISON] = 75;
|
||||
e.vision_range = 6;
|
||||
break;
|
||||
case ENEMY_ORC:
|
||||
e.max_hp = ENEMY_BASE_HP + floor + 4;
|
||||
|
|
@ -104,6 +106,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
|
|||
e.resistance[DMG_PIERCE] = -25;
|
||||
e.resistance[DMG_FIRE] = 0;
|
||||
e.resistance[DMG_POISON] = 0;
|
||||
e.vision_range = 5;
|
||||
break;
|
||||
default:
|
||||
e.max_hp = ENEMY_BASE_HP;
|
||||
|
|
@ -117,6 +120,7 @@ void enemy_spawn(Enemy enemies[], int *count, Map *map, Player *p, int floor) {
|
|||
e.crit_chance = ENEMY_CRIT_CHANCE;
|
||||
e.crit_mult = ENEMY_CRIT_MULT;
|
||||
memset(e.resistance, 0, sizeof(e.resistance));
|
||||
e.vision_range = ENEMY_VIEW_RANGE;
|
||||
break;
|
||||
}
|
||||
e.cooldown = e.speed;
|
||||
|
|
@ -138,7 +142,7 @@ int is_enemy_at(const Enemy *enemies, int count, int x, int y) {
|
|||
|
||||
// Check if enemy can see player (within view range and line of sight)
|
||||
static int can_see_player(Enemy *e, Player *p, Map *map) {
|
||||
return can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, ENEMY_VIEW_RANGE);
|
||||
return can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, e->vision_range);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -190,7 +194,68 @@ static void enemy_patrol(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count
|
|||
}
|
||||
}
|
||||
|
||||
// Perform a single action for an enemy (attack if visible, otherwise patrol)
|
||||
// Move enemy toward last known player position
|
||||
static void enemy_move_to_last_known(Enemy *e, Map *map, Enemy *all_enemies, int enemy_count) {
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
if (e->last_known_x > e->position.x)
|
||||
dx = 1;
|
||||
else if (e->last_known_x < e->position.x)
|
||||
dx = -1;
|
||||
|
||||
if (e->last_known_y > e->position.y)
|
||||
dy = 1;
|
||||
else if (e->last_known_y < e->position.y)
|
||||
dy = -1;
|
||||
|
||||
int new_x = e->position.x + dx;
|
||||
int new_y = e->position.y;
|
||||
|
||||
if (dx != 0 && is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) {
|
||||
e->position.x = new_x;
|
||||
} else if (dy != 0) {
|
||||
new_x = e->position.x;
|
||||
new_y = e->position.y + dy;
|
||||
if (is_floor(map, new_x, new_y) && !is_enemy_at(all_enemies, enemy_count, new_x, new_y)) {
|
||||
e->position.x = new_x;
|
||||
e->position.y = new_y;
|
||||
}
|
||||
}
|
||||
|
||||
if (e->position.x == e->last_known_x && e->position.y == e->last_known_y)
|
||||
e->alert = 0;
|
||||
}
|
||||
|
||||
// Check if position is within alert radius of another enemy
|
||||
static int is_nearby_enemy(const Enemy *enemies, int count, int x, int y, int radius) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!enemies[i].alive)
|
||||
continue;
|
||||
int dx = enemies[i].position.x - x;
|
||||
int dy = enemies[i].position.y - y;
|
||||
if (dx * dx + dy * dy <= radius * radius)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Propagate alert to nearby enemies
|
||||
static void propagate_alert(Enemy *trigger_enemy, Enemy *all_enemies, int enemy_count) {
|
||||
for (int i = 0; i < enemy_count; i++) {
|
||||
Enemy *e = &all_enemies[i];
|
||||
if (!e->alive || e == trigger_enemy)
|
||||
continue;
|
||||
if (e->alert)
|
||||
continue;
|
||||
if (is_nearby_enemy(all_enemies, enemy_count, e->position.x, e->position.y, 5)) {
|
||||
e->alert = 1;
|
||||
e->last_known_x = trigger_enemy->last_known_x;
|
||||
e->last_known_y = trigger_enemy->last_known_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform a single action for an enemy (attack if visible, otherwise patrol or search)
|
||||
void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_count) {
|
||||
if (!e->alive)
|
||||
return;
|
||||
|
|
@ -201,19 +266,34 @@ void enemy_act(Enemy *e, Player *p, Map *map, Enemy *all_enemies, int enemy_coun
|
|||
|
||||
int can_see = can_see_player(e, p, map);
|
||||
|
||||
// If we can see the player, update alert state and last known position
|
||||
if (can_see) {
|
||||
e->alert = 1;
|
||||
e->last_known_x = p->position.x;
|
||||
e->last_known_y = p->position.y;
|
||||
}
|
||||
|
||||
// Attack if adjacent to player
|
||||
if (can_see && can_see_entity(map, e->position.x, e->position.y, p->position.x, p->position.y, 1)) {
|
||||
combat_enemy_attack(e, p);
|
||||
propagate_alert(e, all_enemies, enemy_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move toward player if visible
|
||||
if (can_see) {
|
||||
enemy_move_toward_player(e, p, map, all_enemies, enemy_count);
|
||||
propagate_alert(e, all_enemies, enemy_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Player not visible - patrol randomly
|
||||
// If alert but can't see player, move toward last known position
|
||||
if (e->alert) {
|
||||
enemy_move_to_last_known(e, map, all_enemies, enemy_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Not alert - patrol randomly
|
||||
enemy_patrol(e, map, all_enemies, enemy_count);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ void map_init(Map *map) {
|
|||
map->tiles[y][x] = TILE_WALL;
|
||||
}
|
||||
}
|
||||
memset(map->visible, 0, sizeof(map->visible));
|
||||
memset(map->remembered, 0, sizeof(map->remembered));
|
||||
map->room_count = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue