feat: Implement patrol path markers and enhance enemy movement logic

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 18:48:11 +01:00
parent 4700e669ce
commit c62ea013ba
10 changed files with 393 additions and 85 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:math' as math;
import 'package:test/test.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_entities.dart';
@@ -165,6 +167,80 @@ void main() {
);
},
);
test('alerted dog chase does not increase player distance', () {
final dog = Dog(x: 10.5, y: 10.5, angle: 0, mapId: MapObject.dogStart);
dog
..isAlerted = true
..state = EntityState.patrolling;
final playerPosition = const Coordinate2D(8.5, 10.5);
final double before = dog.position.distanceTo(playerPosition);
final intent = dog.update(
elapsedMs: 1000,
elapsedDeltaMs: 16,
playerPosition: playerPosition,
playerAngle: 0,
isPlayerRunning: false,
isWalkable: (x, y) {
// Block direct west and diagonals to force fallback selection.
if (x == 9 && y == 10) return false;
if (x == 9 && y == 9) return false;
if (x == 9 && y == 11) return false;
return true;
},
areaAt: (_, _) => 0,
isAreaConnectedToPlayer: (_) => true,
onDamagePlayer: (_) {},
tryOpenDoor: (_, _) {},
onPlaySound: (_) {},
);
final after = (dog.position + intent.movement).distanceTo(playerPosition);
expect(after, lessThanOrEqualTo(before + 0.001));
});
test('blocked cardinal movement yields exact zero vector', () {
final dog = Dog(
x: 10.5,
y: 10.5,
angle: -1.57079632679, // north
mapId: MapObject.dogStart,
);
final movement = dog.getValidMovement(
intendedMovement: const Coordinate2D(1e-7, -0.6),
playerPosition: const Coordinate2D(30, 30),
isWalkable: (x, y) {
// Block tile directly north of dog.
if (x == 10 && y == 9) return false;
return true;
},
tryOpenDoor: (_, _) {},
);
expect(movement.x, 0);
expect(movement.y, 0);
});
test('patrol tile-step keeps north heading in map coordinates', () {
final guard = Guard(
x: 20.5,
y: 20.5,
angle: -math.pi / 2,
mapId: MapObject.guardStart,
);
final movement = guard.getPatrolPathMovement(
moveSpeed: 0.1,
playerPosition: const Coordinate2D(40, 40),
isWalkable: (_, _) => true,
tryOpenDoor: (_, _) {},
);
expect(movement.y, lessThan(0));
});
});
}

View File

@@ -0,0 +1,48 @@
import 'dart:math' as math;
import 'package:test/test.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
void main() {
group('Map patrol marker angles', () {
test('maps marker ids 90-97 to T_Path 8-direction semantics', () {
expect(MapObject.patrolAngleForMarker(MapObject.patrolEast), 0.0);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolNorth),
closeTo(-math.pi / 4, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolWest),
closeTo(-math.pi / 2, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolSouth),
closeTo(-3 * math.pi / 4, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolEastAlt),
closeTo(math.pi, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolNorthAlt),
closeTo(3 * math.pi / 4, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolWestAlt),
closeTo(math.pi / 2, 1e-9),
);
expect(
MapObject.patrolAngleForMarker(MapObject.patrolSouthAlt),
closeTo(math.pi / 4, 1e-9),
);
});
test('returns null for non-marker ids', () {
expect(MapObject.patrolAngleForMarker(MapObject.pushwallTrigger), isNull);
expect(MapObject.patrolAngleForMarker(MapObject.playerNorth), isNull);
expect(MapObject.patrolAngleForMarker(0), isNull);
});
});
}