From 301218a01b22732f8030061f875e47bdae07e5ba Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Sun, 15 Mar 2026 19:36:37 +0100 Subject: [PATCH] Slowly fixing enemies Signed-off-by: Hans Kokx --- lib/screens/sprite_gallery.dart | 6 +-- .../lib/src/enemy_map_data.dart | 27 +++++------- .../lib/src/map_objects.dart | 43 ++++++------------- .../lib/src/entities/enemies/enemy.dart | 15 +++---- .../lib/src/entities/enemies/enemy_type.dart | 16 +++---- 5 files changed, 35 insertions(+), 72 deletions(-) diff --git a/lib/screens/sprite_gallery.dart b/lib/screens/sprite_gallery.dart index e5241b2..7bc2b92 100644 --- a/lib/screens/sprite_gallery.dart +++ b/lib/screens/sprite_gallery.dart @@ -38,14 +38,10 @@ class SpriteGallery extends StatelessWidget { } // Append the Map IDs for level editing reference - int staticBase = enemy.mapData.baseStaticId; - int patrolBase = enemy.mapData.basePatrolId; + int staticBase = enemy.mapData.baseId; label += "\nStat: $staticBase (E), ${staticBase + 1} (M), ${staticBase + 2} (H)"; - label += "\nPat: $patrolBase-${patrolBase + 3}(E)"; - label += "\nPat: ${patrolBase + 4}-${patrolBase + 7}(M)"; - label += "\nPat: ${patrolBase + 8}-${patrolBase + 11}(H)"; break; } } diff --git a/packages/wolf_3d_data_types/lib/src/enemy_map_data.dart b/packages/wolf_3d_data_types/lib/src/enemy_map_data.dart index 15ee072..17864ea 100644 --- a/packages/wolf_3d_data_types/lib/src/enemy_map_data.dart +++ b/packages/wolf_3d_data_types/lib/src/enemy_map_data.dart @@ -1,32 +1,25 @@ import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; class EnemyMapData { - final int baseStaticId; - final int basePatrolId; + final int baseId; - const EnemyMapData({required this.baseStaticId, required this.basePatrolId}); + const EnemyMapData(this.baseId); - /// True if this ID belongs to this enemy type at all - bool claimsId(int id) { - bool isStatic = id >= baseStaticId && id <= baseStaticId + 2; - // Patrol blocks contain 12 directional IDs and up to 6 ambush/special IDs - bool isPatrolOrAmbush = id >= basePatrolId && id < basePatrolId + 18; - return isStatic || isPatrolOrAmbush; - } + /// True if the ID falls anywhere within this enemy's 36-ID block + bool claimsId(int id) => id >= baseId && id < baseId + 36; - /// Exact check: Is this a static enemy on this specific difficulty? bool isStaticForDifficulty(int id, Difficulty difficulty) { - return id == baseStaticId + difficulty.level; + int start = baseId + (difficulty.level * 4); + return id >= start && id < start + 4; } - /// Exact check: Is this a patrolling enemy on this specific difficulty? bool isPatrolForDifficulty(int id, Difficulty difficulty) { - int startId = basePatrolId + (difficulty.level * 4); - return id >= startId && id < startId + 4; // Spans 4 directions + int start = baseId + 12 + (difficulty.level * 4); + return id >= start && id < start + 4; } - /// Exact check: Is this an ambush enemy on this specific difficulty? bool isAmbushForDifficulty(int id, Difficulty difficulty) { - return id == basePatrolId + 12 + difficulty.level; + int start = baseId + 24 + (difficulty.level * 4); + return id >= start && id < start + 4; } } diff --git a/packages/wolf_3d_data_types/lib/src/map_objects.dart b/packages/wolf_3d_data_types/lib/src/map_objects.dart index df8cde0..69ec66c 100644 --- a/packages/wolf_3d_data_types/lib/src/map_objects.dart +++ b/packages/wolf_3d_data_types/lib/src/map_objects.dart @@ -95,7 +95,6 @@ abstract class MapObject { static const int deadAardwolf = 125; // Decorative only in WL1 static double getAngle(int id) { - // Player spawn switch (id) { case playerNorth: return CardinalDirection.north.radians; @@ -107,47 +106,29 @@ abstract class MapObject { return CardinalDirection.west.radians; } - // Boss check if (id == bossHansGrosse) return 0.0; final EnemyType? type = EnemyType.fromMapId(id); if (type == null) return 0.0; - // Because the map data strictly groups patrol directions in blocks of 4 - // (East=0, North=1, West=2, South=3), a simple modulo 4 against the base ID - // gives us the exact cardinal direction! - int directionIndex = (id - type.mapData.basePatrolId) % 4; - return CardinalDirection.fromEnemyIndex(directionIndex).radians; + // Because all enemies are in blocks of 4, modulo 4 gets the exact angle + return CardinalDirection.fromEnemyIndex(id % 4).radians; } - /// Only handles the "Is this ID allowed on this difficulty?" math. - /// Does NOT decide IF an object is an enemy or decoration. + /// Determines if an object should be spawned on the current difficulty. static bool isDifficultyAllowed(int objId, Difficulty difficulty) { - if (objId == 124) return true; + // 1. Dead bodies always spawn + if (objId == deadGuard || objId == deadAardwolf) return true; - int? requiredTier; - - // Static Tier Math (IDs 23-54) - if (objId >= 23 && objId <= 54) { - requiredTier = (objId - 23) % 3; - } - // Patrol Tier Math (IDs 108-197) - else if (objId >= 108 && objId <= 197) { - int offsetInType = (objId - 108) % 18; - requiredTier = offsetInType ~/ 4; - } - - if (requiredTier == null) { - // Default to allowed if no tier logic exists + // 2. If it's an enemy, we return true to let it pass through to the + // Enemy.spawn factory. The factory will safely return null if the + // enemy does not belong on this difficulty. + if (EnemyType.fromMapId(objId) != null) { return true; } - int currentTier = switch (difficulty) { - Difficulty.canIPlayDaddy || Difficulty.dontHurtMe => 0, - Difficulty.bringEmOn => 1, - Difficulty.iAmDeathIncarnate => 2, - }; - - return requiredTier == currentTier; + // 3. All non-enemy map objects (keys, ammo, puddles, plants) + // are NOT difficulty-tiered. They always spawn. + return true; } } diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart index eaa0a04..5d62939 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart @@ -186,7 +186,6 @@ abstract class Enemy extends Entity { required void Function(int damage) onDamagePlayer, }); - /// Centralized factory to handle all enemy spawning logic static Enemy? spawn( int objId, double x, @@ -194,6 +193,7 @@ abstract class Enemy extends Entity { Difficulty difficulty, { bool isSharewareMode = false, }) { + // 124 (Dead Guard) famously overwrote a patrol ID in the original engine! if (objId == MapObject.deadGuard || objId == MapObject.deadAardwolf) { return null; } @@ -206,23 +206,20 @@ abstract class Enemy extends Entity { final mapData = type.mapData; - // 1. Validate Difficulty and Determine State + // ALL enemies have explicit directional angles! + double spawnAngle = CardinalDirection.fromEnemyIndex(objId).radians; EntityState spawnState; - double spawnAngle = 0.0; if (mapData.isPatrolForDifficulty(objId, difficulty)) { spawnState = EntityState.patrolling; - spawnAngle = CardinalDirection.fromEnemyIndex(objId).radians; } else if (mapData.isStaticForDifficulty(objId, difficulty)) { - spawnState = EntityState.idle; // Faces the player dynamically later + spawnState = EntityState.idle; } else if (mapData.isAmbushForDifficulty(objId, difficulty)) { - spawnState = EntityState.ambush; // Stays perfectly still until alerted + spawnState = EntityState.ambush; } else { - // If the ID belongs to this enemy but NOT this difficulty, skip spawning! - return null; + return null; // ID belongs to this enemy, but not on this difficulty } - // 2. Return the 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), diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart index 70fb96b..5ecfcc0 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart @@ -45,7 +45,7 @@ class EnemyAnimationMap { enum EnemyType { guard( - mapData: EnemyMapData(baseStaticId: 23, basePatrolId: 108), + mapData: EnemyMapData(108), animations: EnemyAnimationMap( idle: SpriteFrameRange(50, 57), walking: SpriteFrameRange(58, 89), @@ -56,22 +56,18 @@ enum EnemyType { ), ), dog( - mapData: EnemyMapData(baseStaticId: 26, basePatrolId: 126), - // Translated from your old offset logic so the game doesn't break + mapData: EnemyMapData(216), animations: EnemyAnimationMap( idle: SpriteFrameRange(99, 106), walking: SpriteFrameRange(107, 130), attacking: SpriteFrameRange(131, 133), - pain: SpriteFrameRange( - 131, - 131, - ), // Dog shared pain/attack frame in old code + pain: SpriteFrameRange(131, 131), dying: SpriteFrameRange(134, 136), dead: SpriteFrameRange(137, 137), ), ), ss( - mapData: EnemyMapData(baseStaticId: 29, basePatrolId: 144), + mapData: EnemyMapData(180), animations: EnemyAnimationMap( idle: SpriteFrameRange(138, 145), walking: SpriteFrameRange(146, 177), @@ -82,7 +78,7 @@ enum EnemyType { ), ), mutant( - mapData: EnemyMapData(baseStaticId: 32, basePatrolId: 162), + mapData: EnemyMapData(252), animations: EnemyAnimationMap( idle: SpriteFrameRange(187, 194), walking: SpriteFrameRange(195, 226), @@ -93,7 +89,7 @@ enum EnemyType { ), ), officer( - mapData: EnemyMapData(baseStaticId: 35, basePatrolId: 180), + mapData: EnemyMapData(144), animations: EnemyAnimationMap( idle: SpriteFrameRange(238, 245), walking: SpriteFrameRange(246, 277),