diff --git a/lib/features/entities/collectible.dart b/lib/features/entities/collectible.dart index bfa1bf4..e48e789 100644 --- a/lib/features/entities/collectible.dart +++ b/lib/features/entities/collectible.dart @@ -32,8 +32,9 @@ class Collectible extends Entity { int objId, double x, double y, - Difficulty _, - ) { + Difficulty difficulty, { + bool isSharewareMode = false, + }) { if (isCollectible(objId)) { return Collectible( x: x, diff --git a/lib/features/entities/decorative.dart b/lib/features/entities/decorative.dart index 4404cb7..252e2c4 100644 --- a/lib/features/entities/decorative.dart +++ b/lib/features/entities/decorative.dart @@ -39,8 +39,9 @@ class Decorative extends Entity { int objId, double x, double y, - Difficulty _, - ) { + Difficulty difficulty, { + bool isSharewareMode = false, + }) { if (isDecoration(objId)) { return Decorative( x: x, diff --git a/lib/features/entities/enemies/bosses/hans_grosse.dart b/lib/features/entities/enemies/bosses/hans_grosse.dart index 590b289..cc7a6da 100644 --- a/lib/features/entities/enemies/bosses/hans_grosse.dart +++ b/lib/features/entities/enemies/bosses/hans_grosse.dart @@ -35,8 +35,9 @@ class HansGrosse extends Enemy { int objId, double x, double y, - Difficulty difficulty, - ) { + Difficulty difficulty, { + bool isSharewareMode = false, + }) { if (objId == MapObject.bossHansGrosse) { return HansGrosse( x: x, diff --git a/lib/features/entities/enemies/dog.dart b/lib/features/entities/enemies/dog.dart index 147e49e..7f2d52c 100644 --- a/lib/features/entities/enemies/dog.dart +++ b/lib/features/entities/enemies/dog.dart @@ -1,37 +1,26 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; -import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; -import 'package:wolf_dart/features/entities/map_objects.dart'; // NEW class Dog extends Enemy { static const double speed = 0.05; bool _hasBittenThisCycle = false; + static EnemyType get type => EnemyType.dog; + Dog({ required super.x, required super.y, required super.angle, required super.mapId, }) : super( - spriteIndex: EnemyType.dog.spriteBaseIdx, + spriteIndex: type.spriteBaseIdx, state: EntityState.idle, - ); - - static Dog? trySpawn(int objId, double x, double y, Difficulty _) { - if (EnemyType.dog.claimsMapId(objId)) { - bool isPatrolling = objId >= EnemyType.dog.mapBaseId + 18; - - return Dog( - x: x, - y: y, - angle: MapObject.getAngle(objId), - mapId: objId, - )..state = isPatrolling ? EntityState.patrolling : EntityState.idle; - } - return null; + ) { + health = 1; + damage = 5; } @override @@ -39,8 +28,8 @@ class Dog extends Enemy { required int elapsedMs, required Coordinate2D playerPosition, required bool Function(int x, int y) isWalkable, - required void Function(int x, int y) tryOpenDoor, required void Function(int damage) onDamagePlayer, + required void Function(int x, int y) tryOpenDoor, }) { Coordinate2D movement = const Coordinate2D(0, 0); double newAngle = angle; @@ -49,8 +38,6 @@ class Dog extends Enemy { elapsedMs: elapsedMs, playerPosition: playerPosition, isWalkable: isWalkable, - baseReactionMs: 100, - reactionVarianceMs: 200, ); double distance = position.distanceTo(playerPosition); @@ -61,7 +48,6 @@ class Dog extends Enemy { } double diff = angleToPlayer - newAngle; - while (diff <= -math.pi) { diff += 2 * math.pi; } @@ -69,72 +55,35 @@ class Dog extends Enemy { diff -= 2 * math.pi; } - int octant = ((diff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; - if (octant < 0) octant += 8; + EnemyAnimation currentAnim = switch (state) { + EntityState.patrolling => EnemyAnimation.walking, + EntityState.attacking => EnemyAnimation.attacking, + EntityState.dead => isDying ? EnemyAnimation.dying : EnemyAnimation.dead, + _ => EnemyAnimation.idle, + }; - // 3. Clean State Machine - switch (state) { - case EntityState.idle: - spriteIndex = 99 + octant; - break; + spriteIndex = type.getSpriteFromAnimation( + animation: currentAnim, + elapsedMs: elapsedMs, + lastActionTime: lastActionTime, + angleDiff: diff, + ); - case EntityState.patrolling: - if (distance > 0.8) { - // UPGRADED: Smooth vector movement instead of grid-snapping - double moveX = math.cos(angleToPlayer) * speed; - double moveY = math.sin(angleToPlayer) * speed; + if (state == EntityState.patrolling && distance < 1.0) { + state = EntityState.attacking; + lastActionTime = elapsedMs; + _hasBittenThisCycle = false; + } - Coordinate2D intendedMovement = Coordinate2D(moveX, moveY); - - movement = getValidMovement( - intendedMovement, - isWalkable, - tryOpenDoor, - ); - } - - int walkFrame = (elapsedMs ~/ 100) % 4; - spriteIndex = 107 + (walkFrame * 8) + octant; - - if (distance < 1.0 && elapsedMs - lastActionTime > 1000) { - state = EntityState.attacking; - lastActionTime = elapsedMs; - _hasBittenThisCycle = false; - } - break; - - case EntityState.attacking: - int timeAttacking = elapsedMs - lastActionTime; - if (timeAttacking < 200) { - spriteIndex = 139; - if (!_hasBittenThisCycle) { - onDamagePlayer(5); - _hasBittenThisCycle = true; - } - } else { - state = EntityState.patrolling; - lastActionTime = elapsedMs; - } - break; - - // Make sure dogs have a death state so they don't stay standing! - case EntityState.dead: - if (isDying) { - int deathFrame = (elapsedMs - lastActionTime) ~/ 100; - if (deathFrame < 4) { - spriteIndex = - 140 + deathFrame; // Dog death frames usually start here - } else { - spriteIndex = 143; // Dead dog on floor - isDying = false; - } - } else { - spriteIndex = 143; - } - break; - - default: - break; + if (state == EntityState.attacking) { + int time = elapsedMs - lastActionTime; + if (time >= 200 && !_hasBittenThisCycle) { + onDamagePlayer(damage); + _hasBittenThisCycle = true; + } else if (time >= 400) { + state = EntityState.patrolling; + lastActionTime = elapsedMs; + } } return (movement: movement, newAngle: newAngle); diff --git a/lib/features/entities/enemies/enemy.dart b/lib/features/entities/enemies/enemy.dart index 1b6c243..d236ee3 100644 --- a/lib/features/entities/enemies/enemy.dart +++ b/lib/features/entities/enemies/enemy.dart @@ -1,7 +1,23 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; +import 'package:wolf_dart/features/difficulty/difficulty.dart'; +import 'package:wolf_dart/features/entities/enemies/dog.dart'; +import 'package:wolf_dart/features/entities/enemies/guard.dart'; +import 'package:wolf_dart/features/entities/enemies/mutant.dart'; +import 'package:wolf_dart/features/entities/enemies/officer.dart'; +import 'package:wolf_dart/features/entities/enemies/ss.dart'; import 'package:wolf_dart/features/entities/entity.dart'; +import 'package:wolf_dart/features/entities/map_objects.dart'; + +enum EnemyAnimation { + idle, + walking, + attacking, + pain, + dying, + dead, +} enum EnemyType { guard(mapBaseId: 108, spriteBaseIdx: 50), @@ -44,6 +60,93 @@ enum EnemyType { EnemyType.officer => index >= 238 && index <= 287, }; } + + /// Returns the current animation state for a given sprite index. + /// Returns null if the sprite index does not belong to this enemy. + EnemyAnimation? getAnimationFromSprite(int spriteIndex) { + if (!claimsSpriteIndex(spriteIndex)) return null; + + // By working with offsets, we don't have to hardcode the 100+ sprite indices! + int offset = spriteIndex - spriteBaseIdx; + + // All standard enemies use offsets 0-7 for their 8 directional Idle frames + if (offset >= 0 && offset <= 7) return EnemyAnimation.idle; + + // The action frames vary slightly depending on the enemy type + return switch (this) { + EnemyType.guard || EnemyType.ss => switch (offset) { + >= 8 && <= 39 => EnemyAnimation.walking, // 4 frames * 8 directions + >= 40 && <= 42 => EnemyAnimation.attacking, // Aim, Fire, Recoil + 43 => EnemyAnimation.pain, + >= 44 && <= 46 => EnemyAnimation.dying, + _ => EnemyAnimation.dead, // Catch-all for final frames + }, + + EnemyType.officer || EnemyType.mutant => switch (offset) { + >= 8 && <= 39 => EnemyAnimation.walking, + >= 40 && <= 41 => EnemyAnimation.attacking, // Only 2 attack frames! + 42 => EnemyAnimation.pain, + >= 43 && <= 45 => EnemyAnimation.dying, + _ => EnemyAnimation.dead, + }, + + EnemyType.dog => switch (offset) { + // Dogs are special: 3 walk frames (24 total) and NO pain frame! + >= 8 && <= 31 => EnemyAnimation.walking, + >= 32 && <= 34 => EnemyAnimation.attacking, // Leap and bite + >= 35 && <= 37 => EnemyAnimation.dying, + _ => EnemyAnimation.dead, + }, + }; + } + + int getSpriteFromAnimation({ + required EnemyAnimation animation, + required int elapsedMs, + required int lastActionTime, + double angleDiff = 0, + int? walkFrameOverride, // Optional for custom timing + }) { + // 1. Calculate Octant for directional sprites (Idle/Walk) + int octant = ((angleDiff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; + if (octant < 0) octant += 8; + + return switch (animation) { + EnemyAnimation.idle => spriteBaseIdx + octant, + + EnemyAnimation.walking => () { + int frameCount = this == EnemyType.dog ? 3 : 4; + int frame = walkFrameOverride ?? (elapsedMs ~/ 150) % frameCount; + return (spriteBaseIdx + 8) + (frame * 8) + octant; + }(), + + EnemyAnimation.attacking => () { + int time = elapsedMs - lastActionTime; + return switch (this) { + EnemyType.guard || EnemyType.ss || EnemyType.dog => + spriteBaseIdx + + (time < 150 + ? 40 + : time < 300 + ? 41 + : 40), + EnemyType.officer || + EnemyType.mutant => spriteBaseIdx + (time < 200 ? 40 : 41), + }; + }(), + + EnemyAnimation.pain => spriteBaseIdx + (this == EnemyType.dog ? 32 : 42), + + EnemyAnimation.dying => () { + int frame = (elapsedMs - lastActionTime) ~/ 150; + int maxFrames = this == EnemyType.dog ? 2 : 3; + int offset = this == EnemyType.dog ? 35 : 43; + return spriteBaseIdx + offset + (frame.clamp(0, maxFrames)); + }(), + + EnemyAnimation.dead => spriteBaseIdx + (this == EnemyType.dog ? 37 : 45), + }; + } } abstract class Enemy extends Entity { @@ -216,4 +319,34 @@ abstract class Enemy extends Entity { required void Function(int x, int y) tryOpenDoor, required void Function(int damage) onDamagePlayer, }); + + /// Centralized factory to handle all enemy spawning logic + static Enemy? spawn( + int objId, + double x, + double y, + Difficulty difficulty, { + bool isSharewareMode = false, + }) { + // 1. Check Difficulty & Compatibility + if (!MapObject.shouldSpawn(objId, difficulty)) return null; + + // If the checkbox is checked, block non-Shareware enemies + if (isSharewareMode && !MapObject.isSharewareCompatible(objId)) return null; + + final type = EnemyType.fromMapId(objId); + if (type == null) return null; + + bool isPatrolling = objId >= type.mapBaseId + 18; + double spawnAngle = MapObject.getAngle(objId); + + // 2. Return the specific instance + return switch (type) { + EnemyType.guard => Guard(x: x, y: y, angle: spawnAngle, mapId: objId), + EnemyType.dog => Dog(x: x, y: y, angle: spawnAngle, mapId: objId), + EnemyType.ss => SS(x: x, y: y, angle: spawnAngle, mapId: objId), + EnemyType.mutant => Mutant(x: x, y: y, angle: spawnAngle, mapId: objId), + EnemyType.officer => Officer(x: x, y: y, angle: spawnAngle, mapId: objId), + }..state = isPatrolling ? EntityState.patrolling : EntityState.idle; + } } diff --git a/lib/features/entities/enemies/guard.dart b/lib/features/entities/enemies/guard.dart index fc00c7a..6fda7e4 100644 --- a/lib/features/entities/enemies/guard.dart +++ b/lib/features/entities/enemies/guard.dart @@ -1,39 +1,25 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; -import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; -import 'package:wolf_dart/features/entities/map_objects.dart'; class Guard extends Enemy { static const double speed = 0.03; bool _hasFiredThisCycle = false; + static EnemyType get type => EnemyType.guard; + Guard({ required super.x, required super.y, required super.angle, required super.mapId, }) : super( - spriteIndex: EnemyType.guard.spriteBaseIdx, + spriteIndex: type.spriteBaseIdx, state: EntityState.idle, ); - static Guard? trySpawn(int objId, double x, double y, Difficulty _) { - if (EnemyType.guard.claimsMapId(objId) && objId != 124 && objId != 125) { - bool isPatrolling = objId >= EnemyType.guard.mapBaseId + 18; - - return Guard( - x: x, - y: y, - angle: MapObject.getAngle(objId), - mapId: objId, - )..state = isPatrolling ? EntityState.patrolling : EntityState.idle; - } - return null; - } - @override ({Coordinate2D movement, double newAngle}) update({ required int elapsedMs, @@ -58,9 +44,8 @@ class Guard extends Enemy { newAngle = angleToPlayer; } - // Octant logic (Directional sprites) + // Calculate angle diff for the octant logic double diff = angleToPlayer - newAngle; - while (diff <= -math.pi) { diff += 2 * math.pi; } @@ -68,82 +53,32 @@ class Guard extends Enemy { diff -= 2 * math.pi; } - int octant = ((diff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; - if (octant < 0) octant += 8; + // Helper to get sprite based on current state + EnemyAnimation currentAnim = switch (state) { + EntityState.patrolling => EnemyAnimation.walking, + EntityState.attacking => EnemyAnimation.attacking, + EntityState.pain => EnemyAnimation.pain, + EntityState.dead => isDying ? EnemyAnimation.dying : EnemyAnimation.dead, + _ => EnemyAnimation.idle, + }; - // 3. State Machine - switch (state) { - case EntityState.idle: - spriteIndex = 50 + octant; - break; + spriteIndex = type.getSpriteFromAnimation( + animation: currentAnim, + elapsedMs: elapsedMs, + lastActionTime: lastActionTime, + angleDiff: diff, + ); - case EntityState.patrolling: - if (distance > 0.8) { - double moveX = math.cos(angleToPlayer) * speed; - double moveY = math.sin(angleToPlayer) * speed; - Coordinate2D intendedMovement = Coordinate2D(moveX, moveY); - - movement = getValidMovement( - intendedMovement, - isWalkable, - tryOpenDoor, - ); - } - - int walkFrame = (elapsedMs ~/ 150) % 4; - spriteIndex = 58 + (walkFrame * 8) + octant; - - if (distance < 6.0 && elapsedMs - lastActionTime > 1500) { - if (hasLineOfSight(playerPosition, isWalkable)) { - state = EntityState.attacking; - lastActionTime = elapsedMs; - _hasFiredThisCycle = false; - } - } - break; - - case EntityState.attacking: - int timeShooting = elapsedMs - lastActionTime; - if (timeShooting < 150) { - spriteIndex = 90; // Aiming - } else if (timeShooting < 300) { - spriteIndex = 91; // Firing - if (!_hasFiredThisCycle) { - onDamagePlayer(10); - _hasFiredThisCycle = true; - } - } else if (timeShooting < 450) { - spriteIndex = 90; // Recoil (back to aim pose) - } else { - state = EntityState.patrolling; - lastActionTime = elapsedMs; - } - break; - - case EntityState.pain: - spriteIndex = 92; // Ouch frame - if (elapsedMs - lastActionTime > 250) { - state = EntityState.patrolling; - lastActionTime = elapsedMs; - } - break; - - case EntityState.dead: - if (isDying) { - int deathFrame = (elapsedMs - lastActionTime) ~/ 150; - if (deathFrame < 3) { - spriteIndex = 93 + deathFrame; // Cycles 93, 94, 95 - } else { - spriteIndex = 95; // Final dead frame - isDying = false; - } - } else { - spriteIndex = 95; // Final dead frame - } - break; - - default: - break; + // Logic triggers (Damage, State transitions) + if (state == EntityState.attacking) { + int time = elapsedMs - lastActionTime; + if (time >= 150 && time < 300 && !_hasFiredThisCycle) { + onDamagePlayer(10); + _hasFiredThisCycle = true; + } else if (time >= 450) { + state = EntityState.patrolling; + lastActionTime = elapsedMs; + } } return (movement: movement, newAngle: newAngle); diff --git a/lib/features/entities/enemies/mutant.dart b/lib/features/entities/enemies/mutant.dart index 3e1a92a..a932da7 100644 --- a/lib/features/entities/enemies/mutant.dart +++ b/lib/features/entities/enemies/mutant.dart @@ -1,42 +1,28 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; -import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; -import 'package:wolf_dart/features/entities/map_objects.dart'; class Mutant extends Enemy { - static const double speed = 0.045; + static const double speed = 0.04; bool _hasFiredThisCycle = false; + static EnemyType get type => EnemyType.mutant; + Mutant({ required super.x, required super.y, required super.angle, required super.mapId, }) : super( - spriteIndex: EnemyType.mutant.spriteBaseIdx, + spriteIndex: type.spriteBaseIdx, state: EntityState.idle, ) { health = 45; damage = 10; } - static Mutant? trySpawn(int objId, double x, double y, Difficulty _) { - if (EnemyType.mutant.claimsMapId(objId)) { - bool isPatrolling = objId >= EnemyType.mutant.mapBaseId + 18; - - return Mutant( - x: x, - y: y, - angle: MapObject.getAngle(objId), - mapId: objId, - )..state = isPatrolling ? EntityState.patrolling : EntityState.idle; - } - return null; - } - @override ({Coordinate2D movement, double newAngle}) update({ required int elapsedMs, @@ -48,7 +34,6 @@ class Mutant extends Enemy { Coordinate2D movement = const Coordinate2D(0, 0); double newAngle = angle; - // Mutants don't make wake-up noises in the original game! checkWakeUp( elapsedMs: elapsedMs, playerPosition: playerPosition, @@ -62,6 +47,7 @@ class Mutant extends Enemy { newAngle = angleToPlayer; } + // Calculate angle diff for the octant logic double diff = angleToPlayer - newAngle; while (diff <= -math.pi) { diff += 2 * math.pi; @@ -69,80 +55,31 @@ class Mutant extends Enemy { while (diff > math.pi) { diff -= 2 * math.pi; } - int octant = ((diff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; - if (octant < 0) octant += 8; - switch (state) { - case EntityState.idle: - spriteIndex = EnemyType.mutant.spriteBaseIdx + octant; - break; + EnemyAnimation currentAnim = switch (state) { + EntityState.patrolling => EnemyAnimation.walking, + EntityState.attacking => EnemyAnimation.attacking, + EntityState.pain => EnemyAnimation.pain, + EntityState.dead => isDying ? EnemyAnimation.dying : EnemyAnimation.dead, + _ => EnemyAnimation.idle, + }; - case EntityState.patrolling: - if (distance > 0.8) { - double moveX = math.cos(angleToPlayer) * speed; - double moveY = math.sin(angleToPlayer) * speed; - movement = getValidMovement( - Coordinate2D(moveX, moveY), - isWalkable, - tryOpenDoor, - ); - } + spriteIndex = type.getSpriteFromAnimation( + animation: currentAnim, + elapsedMs: elapsedMs, + lastActionTime: lastActionTime, + angleDiff: diff, + ); - int walkFrame = (elapsedMs ~/ 150) % 4; - spriteIndex = - (EnemyType.mutant.spriteBaseIdx + 8) + (walkFrame * 8) + octant; - - if (distance < 6.0 && elapsedMs - lastActionTime > 1000) { - if (hasLineOfSight(playerPosition, isWalkable)) { - state = EntityState.attacking; - lastActionTime = elapsedMs; - _hasFiredThisCycle = false; - } - } - break; - - case EntityState.attacking: - int timeShooting = elapsedMs - lastActionTime; - if (timeShooting < 150) { - spriteIndex = EnemyType.mutant.spriteBaseIdx + 46; // Aiming - } else if (timeShooting < 300) { - spriteIndex = EnemyType.mutant.spriteBaseIdx + 47; // Firing - if (!_hasFiredThisCycle) { - onDamagePlayer(damage); - _hasFiredThisCycle = true; - } - } else if (timeShooting < 450) { - spriteIndex = EnemyType.mutant.spriteBaseIdx + 48; // Recoil - } else { - state = EntityState.patrolling; - lastActionTime = elapsedMs; - } - break; - - case EntityState.pain: - spriteIndex = EnemyType.mutant.spriteBaseIdx + 44; - if (elapsedMs - lastActionTime > 250) { - state = EntityState.patrolling; - lastActionTime = elapsedMs; - } - break; - - case EntityState.dead: - if (isDying) { - int deathFrame = (elapsedMs - lastActionTime) ~/ 150; - if (deathFrame < 4) { - spriteIndex = (EnemyType.mutant.spriteBaseIdx + 40) + deathFrame; - } else { - spriteIndex = EnemyType.mutant.spriteBaseIdx + 45; - isDying = false; - } - } else { - spriteIndex = EnemyType.mutant.spriteBaseIdx + 45; - } - break; - - default: - break; + if (state == EntityState.attacking) { + int time = elapsedMs - lastActionTime; + if (time >= 150 && !_hasFiredThisCycle) { + onDamagePlayer(damage); + _hasFiredThisCycle = true; + } else if (time >= 300) { + state = EntityState.patrolling; + lastActionTime = elapsedMs; + } } return (movement: movement, newAngle: newAngle); diff --git a/lib/features/entities/enemies/officer.dart b/lib/features/entities/enemies/officer.dart index 581a2a1..abaf4b9 100644 --- a/lib/features/entities/enemies/officer.dart +++ b/lib/features/entities/enemies/officer.dart @@ -1,10 +1,8 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; -import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; -import 'package:wolf_dart/features/entities/map_objects.dart'; class Officer extends Enemy { static const double speed = 0.055; @@ -23,20 +21,6 @@ class Officer extends Enemy { damage = 15; } - static Officer? trySpawn(int objId, double x, double y, Difficulty _) { - if (EnemyType.officer.claimsMapId(objId)) { - bool isPatrolling = objId >= EnemyType.officer.mapBaseId + 18; - - return Officer( - x: x, - y: y, - angle: MapObject.getAngle(objId), - mapId: objId, - )..state = isPatrolling ? EntityState.patrolling : EntityState.idle; - } - return null; - } - @override ({Coordinate2D movement, double newAngle}) update({ required int elapsedMs, diff --git a/lib/features/entities/enemies/ss.dart b/lib/features/entities/enemies/ss.dart index e7329ab..e85001b 100644 --- a/lib/features/entities/enemies/ss.dart +++ b/lib/features/entities/enemies/ss.dart @@ -1,10 +1,8 @@ import 'dart:math' as math; import 'package:wolf_dart/classes/coordinate_2d.dart'; -import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; import 'package:wolf_dart/features/entities/entity.dart'; -import 'package:wolf_dart/features/entities/map_objects.dart'; class SS extends Enemy { static const double speed = 0.04; @@ -23,20 +21,6 @@ class SS extends Enemy { damage = 20; } - static SS? trySpawn(int objId, double x, double y, Difficulty _) { - if (EnemyType.ss.claimsMapId(objId)) { - bool isPatrolling = objId >= EnemyType.ss.mapBaseId + 18; - - return SS( - x: x, - y: y, - angle: MapObject.getAngle(objId), - mapId: objId, - )..state = isPatrolling ? EntityState.patrolling : EntityState.idle; - } - return null; - } - @override ({Coordinate2D movement, double newAngle}) update({ required int elapsedMs, diff --git a/lib/features/entities/entity_registry.dart b/lib/features/entities/entity_registry.dart index a27514e..488a5a2 100644 --- a/lib/features/entities/entity_registry.dart +++ b/lib/features/entities/entity_registry.dart @@ -2,12 +2,7 @@ import 'package:wolf_dart/features/difficulty/difficulty.dart'; import 'package:wolf_dart/features/entities/collectible.dart'; import 'package:wolf_dart/features/entities/decorative.dart'; import 'package:wolf_dart/features/entities/enemies/bosses/hans_grosse.dart'; -import 'package:wolf_dart/features/entities/enemies/dog.dart'; import 'package:wolf_dart/features/entities/enemies/enemy.dart'; -import 'package:wolf_dart/features/entities/enemies/guard.dart'; -import 'package:wolf_dart/features/entities/enemies/mutant.dart'; -import 'package:wolf_dart/features/entities/enemies/officer.dart'; -import 'package:wolf_dart/features/entities/enemies/ss.dart'; import 'package:wolf_dart/features/entities/entity.dart'; import 'package:wolf_dart/features/entities/map_objects.dart'; @@ -16,17 +11,14 @@ typedef EntitySpawner = int objId, double x, double y, - Difficulty difficulty, - ); + Difficulty difficulty, { + bool isSharewareMode, + }); abstract class EntityRegistry { static final List _spawners = [ // Enemies need to try to spawn first - Guard.trySpawn, - Officer.trySpawn, - SS.trySpawn, - Mutant.trySpawn, - Dog.trySpawn, + Enemy.spawn, // Bosses HansGrosse.trySpawn, @@ -41,11 +33,15 @@ abstract class EntityRegistry { double x, double y, Difficulty difficulty, - int maxSprites, - ) { + int maxSprites, { + bool isSharewareMode = false, + }) { // 1. Difficulty check before even looking for a spawner if (!MapObject.shouldSpawn(objId, difficulty)) return null; + // If the checkbox is checked, block non-Shareware enemies + if (isSharewareMode && !MapObject.isSharewareCompatible(objId)) return null; + if (objId == 0) return null; for (final spawner in _spawners) { diff --git a/lib/features/map/wolf_map_parser.dart b/lib/features/map/wolf_map_parser.dart index eec13ba..adc9dc0 100644 --- a/lib/features/map/wolf_map_parser.dart +++ b/lib/features/map/wolf_map_parser.dart @@ -9,7 +9,7 @@ abstract class WolfMapParser { static List parseMaps( ByteData mapHead, ByteData gameMaps, { - bool isShareware = false, + bool isShareware = true, }) { List levels = []; diff --git a/lib/features/renderer/renderer.dart b/lib/features/renderer/renderer.dart index bb7eb7a..4f54bec 100644 --- a/lib/features/renderer/renderer.dart +++ b/lib/features/renderer/renderer.dart @@ -109,6 +109,7 @@ class _WolfRendererState extends State y + 0.5, widget.difficulty, gameMap.sprites.length, + isSharewareMode: isShareware, ); if (newEntity != null) {