Enemies now react to shots being fired in the same area

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-17 14:10:25 +01:00
parent 9c60c81da5
commit 0e77127585

View File

@@ -75,6 +75,9 @@ class WolfEngine {
bool isInitialized = false; bool isInitialized = false;
/// Tracks the last time a sound wave was emitted to prevent BFS spam.
int _lastAcousticAlertTime = 0;
/// Initializes the engine, sets the starting episode, and loads the first level. /// Initializes the engine, sets the starting episode, and loads the first level.
void init() { void init() {
_currentEpisodeIndex = startingEpisode; _currentEpisodeIndex = startingEpisode;
@@ -222,8 +225,15 @@ class WolfEngine {
if (input.requestedWeapon != null) { if (input.requestedWeapon != null) {
player.requestWeaponSwitch(input.requestedWeapon!); player.requestWeaponSwitch(input.requestedWeapon!);
} }
if (input.isFiring) { if (input.isFiring) {
player.fire(_timeAliveMs); player.fire(_timeAliveMs);
// Throttle the acoustic flood-fill to emit a "wave" every 400ms while firing
if (_timeAliveMs - _lastAcousticAlertTime > 400) {
_propagateGunfire();
_lastAcousticAlertTime = _timeAliveMs;
}
} else { } else {
player.releaseTrigger(); player.releaseTrigger();
} }
@@ -363,6 +373,70 @@ class WolfEngine {
if (itemsToAdd.isNotEmpty) entities.addAll(itemsToAdd); if (itemsToAdd.isNotEmpty) entities.addAll(itemsToAdd);
} }
/// Propagates weapon noise through corridors and open doors using a Breadth-First Search.
void _propagateGunfire() {
int maxAcousticRange = 20; // How many tiles the sound wave travels
int startX = player.x.toInt();
int startY = player.y.toInt();
// Track visited tiles using a 1D index to prevent infinite loops
Set<int> visited = {startY * 64 + startX};
List<({int x, int y})> queue = [(x: startX, y: startY)];
int distance = 0;
while (queue.isNotEmpty && distance < maxAcousticRange) {
List<({int x, int y})> nextQueue = [];
for (var tile in queue) {
// 1. Alert any enemies standing on this specific tile
for (var entity in entities) {
if (entity is Enemy &&
!entity.isAlerted &&
entity.state != EntityState.dead) {
if (entity.position.x.toInt() == tile.x &&
entity.position.y.toInt() == tile.y) {
entity.isAlerted = true;
// Wake them up!
if (entity.state == EntityState.idle ||
entity.state == EntityState.ambush) {
entity.state = EntityState.patrolling;
entity.lastActionTime = _timeAliveMs;
}
}
}
}
// 2. Expand the sound wave outward to North, East, South, West
final neighbors = <({int x, int y})>[
(x: tile.x + 1, y: tile.y),
(x: tile.x - 1, y: tile.y),
(x: tile.x, y: tile.y + 1),
(x: tile.x, y: tile.y - 1),
];
for (var n in neighbors) {
// Keep it within the 64x64 grid limits
if (n.x >= 0 && n.x < 64 && n.y >= 0 && n.y < 64) {
int idx = n.y * 64 + n.x;
if (!visited.contains(idx)) {
visited.add(idx);
// Sound only travels through walkable tiles (air and OPEN doors).
if (isWalkable(n.x, n.y)) {
nextQueue.add(n);
}
}
}
}
}
queue = nextQueue;
distance++;
}
}
/// Returns true if a tile is empty or contains a door that is sufficiently open. /// Returns true if a tile is empty or contains a door that is sufficiently open.
bool isWalkable(int x, int y) { bool isWalkable(int x, int y) {
if (currentLevel[y][x] == 0) return true; if (currentLevel[y][x] == 0) return true;