diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/bosses/hans_grosse.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/bosses/hans_grosse.dart index 0539d95..7685704 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/bosses/hans_grosse.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/bosses/hans_grosse.dart @@ -96,12 +96,13 @@ class HansGrosse extends Enemy { if (!isAlerted || distance > 1.5) { double currentMoveAngle = isAlerted ? newAngle : angle; movement = getValidMovement( - Coordinate2D( + intendedMovement: Coordinate2D( math.cos(currentMoveAngle) * speed, math.sin(currentMoveAngle) * speed, ), - isWalkable, - tryOpenDoor, + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); } diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/dog.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/dog.dart index c02d8a1..0fc4b69 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/dog.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/dog.dart @@ -7,7 +7,9 @@ import 'package:wolf_3d_dart/src/entities/entity.dart'; import 'package:wolf_3d_dart/wolf_3d_data_types.dart'; class Dog extends Enemy { - static const double speed = 0.05; + /// Original SPDDOG is 1024. 1 Tile is 65536 units. + /// 1024 / 65536 = ~0.0156 tiles per tic. + static const double speedPerTic = 0.0156; @override EnemyType get type => EnemyType.dog; @@ -21,8 +23,7 @@ class Dog extends Enemy { spriteIndex: EnemyType.dog.animations.idle.start, state: EntityState.idle, ) { - health = 1; - damage = 2; + health = 1; // Dogs always have 1 HP in the original engine } @override @@ -36,19 +37,96 @@ class Dog extends Enemy { }) { Coordinate2D movement = const Coordinate2D(0, 0); double newAngle = angle; + double distance = position.distanceTo(playerPosition); + // 1. Perception checkWakeUp( elapsedMs: elapsedMs, playerPosition: playerPosition, isWalkable: isWalkable, ); - double distance = position.distanceTo(playerPosition); - double angleToPlayer = position.angleTo(playerPosition); + // 2. Discrete AI Decision Rhythm + bool ticReady = processTics(elapsedDeltaMs, moveSpeed: 0); - if (isAlerted && state != EntityState.dead) newAngle = angleToPlayer; + if (state == EntityState.attacking) { + // --- AUTHENTIC T_Bite / Jump Sequence --- + // Original Jump sequence (s_dogjump1 to s_dogjump5) is 5 frames, 10 tics each + if (ticReady) { + currentFrame++; - double diff = angleToPlayer - newAngle; + if (currentFrame == 1) { + // Phase 2: The actual bite (s_dogjump2) + // Original hit chance is US_RndT() < 180 (~70% chance) + if (distance <= 1.2 && math.Random().nextDouble() < (180 / 256)) { + // Original damage: US_RndT() >> 4 (0 to 15 damage) + int actualDamage = math.Random().nextInt(16); + onDamagePlayer(actualDamage); + } + setTics(10); + } else if (currentFrame < 5) { + setTics(10); // Phases 3-5: Mid-air and Landing + } else { + // Sequence complete, return to chase + state = EntityState.patrolling; + currentFrame = 0; + setTics(10); + } + } + } else if (state != EntityState.dead) { + // 3. Continuous Movement (T_DogChase / T_Path) + double ticsThisFrame = elapsedDeltaMs / 14.28; + double currentMoveSpeed = speedPerTic * ticsThisFrame; + + if (isAlerted) { + newAngle = position.angleTo(playerPosition); + + // Trigger Jump: Original uses MINACTORDIST (approx 0.8 tiles) + if (distance <= 0.8) { + state = EntityState.attacking; + currentFrame = 0; + lastActionTime = elapsedMs; + setTics(10); + return (movement: const Coordinate2D(0, 0), newAngle: newAngle); + } + + movement = getValidMovement( + intendedMovement: Coordinate2D( + math.cos(newAngle) * currentMoveSpeed, + math.sin(newAngle) * currentMoveSpeed, + ), + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, + ); + } else if (state == EntityState.patrolling) { + movement = getValidMovement( + intendedMovement: Coordinate2D( + math.cos(angle) * currentMoveSpeed, + math.sin(angle) * currentMoveSpeed, + ), + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, + ); + } + + if (ticReady) { + currentFrame = (currentFrame + 1) % 4; + setTics(10); // Chase rhythm (s_dogchase1-4) + } + } + + _updateAnimation(elapsedMs, newAngle, playerPosition); + return (movement: movement, newAngle: newAngle); + } + + void _updateAnimation( + int elapsedMs, + double newAngle, + Coordinate2D playerPosition, + ) { + double diff = position.angleTo(playerPosition) - newAngle; while (diff <= -math.pi) { diff += 2 * math.pi; } @@ -68,51 +146,9 @@ class Dog extends Enemy { elapsedMs: elapsedMs, lastActionTime: lastActionTime, angleDiff: diff, - walkFrameOverride: state == EntityState.patrolling ? currentFrame : null, + walkFrameOverride: (state == EntityState.patrolling || isAlerted) + ? currentFrame + : null, ); - - if (state == EntityState.patrolling) { - if (!isAlerted || distance > 1.0) { - double currentMoveAngle = isAlerted ? angleToPlayer : angle; - movement = getValidMovement( - Coordinate2D( - math.cos(currentMoveAngle) * speed, - math.sin(currentMoveAngle) * speed, - ), - isWalkable, - tryOpenDoor, - ); - } - - if (processTics(elapsedDeltaMs, moveSpeed: speed)) { - currentFrame = (currentFrame + 1) % 4; - setTics(5); - - if (isAlerted && distance < 1.0) { - state = EntityState.attacking; - currentFrame = 0; - lastActionTime = elapsedMs; - setTics(5); // Leap - } - } - } - - if (state == EntityState.attacking) { - if (processTics(elapsedDeltaMs, moveSpeed: 0)) { - currentFrame++; - if (currentFrame == 1) { - onDamagePlayer(damage); // Bite - setTics(5); - } else if (currentFrame == 2) { - setTics(5); // Land - } else { - state = EntityState.patrolling; - currentFrame = 0; - setTics(5); - } - } - } - - return (movement: movement, newAngle: newAngle); } } diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy.dart index c51ea48..638719a 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy.dart @@ -240,11 +240,23 @@ abstract class Enemy extends Entity { /// The logic performs separate X and Y collision checks to allow "sliding" along /// walls. If a movement is blocked, it calls [tryOpenDoor] to simulate the /// enemy's ability to navigate through the level. - Coordinate2D getValidMovement( - Coordinate2D intendedMovement, - bool Function(int x, int y) isWalkable, - void Function(int x, int y) tryOpenDoor, - ) { + Coordinate2D getValidMovement({ + required Coordinate2D intendedMovement, + required Coordinate2D playerPosition, + required bool Function(int x, int y) isWalkable, + required void Function(int x, int y) tryOpenDoor, + }) { + final double distToPlayer = position.distanceTo(playerPosition); + const double minDistance = 0.9; + + if (distToPlayer < minDistance) { + // If already too close, only allow movement if it increases distance + Coordinate2D nextPos = position + intendedMovement; + if (nextPos.distanceTo(playerPosition) < distToPlayer) { + return const Coordinate2D(0, 0); + } + } + double newX = position.x + intendedMovement.x; double newY = position.y + intendedMovement.y; diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy_type.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy_type.dart index 09b2e0b..1967cda 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy_type.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/enemy_type.dart @@ -28,7 +28,7 @@ enum EnemyType { idle: SpriteFrameRange(99, 106), walking: SpriteFrameRange(107, 130), attacking: SpriteFrameRange(135, 137), - pain: SpriteFrameRange(137, 137), + pain: SpriteFrameRange(0, 0), dying: SpriteFrameRange(131, 133), dead: SpriteFrameRange(134, 134), ), diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/guard.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/guard.dart index d0671ff..98c1db2 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/guard.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/guard.dart @@ -86,19 +86,25 @@ class Guard extends Enemy { } // Pursuit movement - movement = _calculateMovement( - newAngle, - currentMoveSpeed, - isWalkable, - tryOpenDoor, + movement = getValidMovement( + intendedMovement: Coordinate2D( + math.cos(newAngle) * currentMoveSpeed, + math.sin(newAngle) * currentMoveSpeed, + ), + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); } else if (state == EntityState.patrolling) { // Normal patrol movement - movement = _calculateMovement( - angle, - currentMoveSpeed, - isWalkable, - tryOpenDoor, + movement = getValidMovement( + intendedMovement: Coordinate2D( + math.cos(angle) * currentMoveSpeed, + math.sin(angle) * currentMoveSpeed, + ), + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); } @@ -112,22 +118,6 @@ class Guard extends Enemy { return (movement: movement, newAngle: newAngle); } - Coordinate2D _calculateMovement( - double moveAngle, - double moveSpeed, - bool Function(int x, int y) isWalkable, - void Function(int x, int y) tryOpenDoor, - ) { - return getValidMovement( - Coordinate2D( - math.cos(moveAngle) * moveSpeed, - math.sin(moveAngle) * moveSpeed, - ), - isWalkable, - tryOpenDoor, - ); - } - void _updateAnimation( int elapsedMs, double newAngle, diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/mutant.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/mutant.dart index 4e1bd32..3ee76d2 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/mutant.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/mutant.dart @@ -76,12 +76,13 @@ class Mutant extends Enemy { if (!isAlerted || distance > 0.8) { double currentMoveAngle = isAlerted ? angleToPlayer : angle; movement = getValidMovement( - Coordinate2D( + intendedMovement: Coordinate2D( math.cos(currentMoveAngle) * speed, math.sin(currentMoveAngle) * speed, ), - isWalkable, - tryOpenDoor, + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); } diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/officer.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/officer.dart index d673d97..0e05698 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/officer.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/officer.dart @@ -76,12 +76,13 @@ class Officer extends Enemy { if (!isAlerted || distance > 0.8) { double currentMoveAngle = isAlerted ? angleToPlayer : angle; movement = getValidMovement( - Coordinate2D( + intendedMovement: Coordinate2D( math.cos(currentMoveAngle) * speed, math.sin(currentMoveAngle) * speed, ), - isWalkable, - tryOpenDoor, + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); } diff --git a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/ss.dart b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/ss.dart index 09736aa..80dc060 100644 --- a/packages/wolf_3d_dart/lib/src/entities/entities/enemies/ss.dart +++ b/packages/wolf_3d_dart/lib/src/entities/entities/enemies/ss.dart @@ -75,12 +75,13 @@ class SS extends Enemy { if (!isAlerted || distance > 0.8) { double currentMoveAngle = isAlerted ? angleToPlayer : angle; movement = getValidMovement( - Coordinate2D( + intendedMovement: Coordinate2D( math.cos(currentMoveAngle) * speed, math.sin(currentMoveAngle) * speed, ), - isWalkable, - tryOpenDoor, + playerPosition: playerPosition, + isWalkable: isWalkable, + tryOpenDoor: tryOpenDoor, ); }