From 0eebf8e4fa6b4b1b515cb84a9a644e552732c32c Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Sun, 15 Mar 2026 19:21:12 +0100 Subject: [PATCH] Mapping enemy ids to difficulties Signed-off-by: Hans Kokx --- lib/screens/sprite_gallery.dart | 10 + .../lib/src/enemy_map_data.dart | 32 ++++ .../lib/src/map_objects.dart | 9 +- .../lib/src/sprite_frame_range.dart | 10 + .../lib/wolf_3d_data_types.dart | 2 + .../lib/src/entities/enemies/dog.dart | 10 +- .../lib/src/entities/enemies/enemy.dart | 178 ++---------------- .../src/entities/enemies/enemy_animation.dart | 1 + .../lib/src/entities/enemies/enemy_type.dart | 169 +++++++++++++++++ .../lib/src/entities/enemies/guard.dart | 8 +- .../lib/src/entities/enemies/mutant.dart | 10 +- .../lib/src/entities/enemies/officer.dart | 17 +- .../lib/src/entities/enemies/ss.dart | 10 +- packages/wolf_3d_entities/lib/src/entity.dart | 2 +- .../lib/wolf_3d_entities.dart | 2 + 15 files changed, 274 insertions(+), 196 deletions(-) create mode 100644 packages/wolf_3d_data_types/lib/src/enemy_map_data.dart create mode 100644 packages/wolf_3d_data_types/lib/src/sprite_frame_range.dart create mode 100644 packages/wolf_3d_entities/lib/src/entities/enemies/enemy_animation.dart create mode 100644 packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart diff --git a/lib/screens/sprite_gallery.dart b/lib/screens/sprite_gallery.dart index 8746121..e5241b2 100644 --- a/lib/screens/sprite_gallery.dart +++ b/lib/screens/sprite_gallery.dart @@ -36,6 +36,16 @@ class SpriteGallery extends StatelessWidget { if (animation != null) { label += "\n${animation.name}"; } + + // Append the Map IDs for level editing reference + int staticBase = enemy.mapData.baseStaticId; + int patrolBase = enemy.mapData.basePatrolId; + + 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 new file mode 100644 index 0000000..15ee072 --- /dev/null +++ b/packages/wolf_3d_data_types/lib/src/enemy_map_data.dart @@ -0,0 +1,32 @@ +import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; + +class EnemyMapData { + final int baseStaticId; + final int basePatrolId; + + const EnemyMapData({required this.baseStaticId, required this.basePatrolId}); + + /// 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; + } + + /// Exact check: Is this a static enemy on this specific difficulty? + bool isStaticForDifficulty(int id, Difficulty difficulty) { + return id == baseStaticId + difficulty.level; + } + + /// 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 + } + + /// Exact check: Is this an ambush enemy on this specific difficulty? + bool isAmbushForDifficulty(int id, Difficulty difficulty) { + return id == basePatrolId + 12 + difficulty.level; + } +} 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 0a6923f..df8cde0 100644 --- a/packages/wolf_3d_data_types/lib/src/map_objects.dart +++ b/packages/wolf_3d_data_types/lib/src/map_objects.dart @@ -111,11 +111,12 @@ abstract class MapObject { if (id == bossHansGrosse) return 0.0; final EnemyType? type = EnemyType.fromMapId(id); - if (type == null) return 0.0; // Not a standard directional enemy + if (type == null) return 0.0; - // Normalize patrolling enemies back to the standing block, THEN get the - // 4-way angle - int directionIndex = ((id - type.patrolId) % 18) % 4; + // 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; } diff --git a/packages/wolf_3d_data_types/lib/src/sprite_frame_range.dart b/packages/wolf_3d_data_types/lib/src/sprite_frame_range.dart new file mode 100644 index 0000000..937238c --- /dev/null +++ b/packages/wolf_3d_data_types/lib/src/sprite_frame_range.dart @@ -0,0 +1,10 @@ +/// Defines the exact start and end sprite indices for an animation state. +class SpriteFrameRange { + final int start; + final int end; + + const SpriteFrameRange(this.start, this.end); + + int get length => end - start + 1; + bool contains(int index) => index >= start && index <= end; +} diff --git a/packages/wolf_3d_data_types/lib/wolf_3d_data_types.dart b/packages/wolf_3d_data_types/lib/wolf_3d_data_types.dart index defb0a7..08e7c00 100644 --- a/packages/wolf_3d_data_types/lib/wolf_3d_data_types.dart +++ b/packages/wolf_3d_data_types/lib/wolf_3d_data_types.dart @@ -6,6 +6,7 @@ library; export 'src/cardinal_direction.dart' show CardinalDirection; export 'src/coordinate_2d.dart' show Coordinate2D; export 'src/difficulty.dart' show Difficulty; +export 'src/enemy_map_data.dart' show EnemyMapData; export 'src/episode.dart' show Episode; export 'src/game_file.dart' show GameFile; export 'src/game_version.dart' show GameVersion; @@ -14,5 +15,6 @@ export 'src/map_objects.dart' show MapObject; export 'src/sound.dart' show PcmSound, AdLibSound, ImfMusic, ImfInstruction, WolfMusicMap; export 'src/sprite.dart' hide Matrix; +export 'src/sprite_frame_range.dart' show SpriteFrameRange; export 'src/wolf_level.dart' show WolfLevel; export 'src/wolfenstein_data.dart' show WolfensteinData; diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/dog.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/dog.dart index 76bc177..9711c5b 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/dog.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/dog.dart @@ -2,6 +2,8 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/enemy.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entity.dart'; class Dog extends Enemy { @@ -15,7 +17,7 @@ class Dog extends Enemy { required super.y, required super.angle, required super.mapId, - }) : super(spriteIndex: type.spriteBaseIdx, state: EntityState.idle) { + }) : super(spriteIndex: type.animations.idle.start, state: EntityState.idle) { health = 1; damage = 5; } @@ -40,7 +42,7 @@ class Dog extends Enemy { double distance = position.distanceTo(playerPosition); double angleToPlayer = position.angleTo(playerPosition); - if (state != EntityState.idle && state != EntityState.dead) { + if (isAlerted && state != EntityState.dead) { newAngle = angleToPlayer; } @@ -66,10 +68,6 @@ class Dog extends Enemy { angleDiff: diff, ); - if (isAlerted && state != EntityState.dead) { - newAngle = angleToPlayer; - } - // Dogs attack based on distance, so wrap the movement and attack in alert checks if (state == EntityState.patrolling) { if (!isAlerted || distance > 1.0) { 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 146a73d..eaa0a04 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy.dart @@ -2,159 +2,13 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/dog.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entities/enemies/guard.dart'; import 'package:wolf_3d_entities/src/entities/enemies/mutant.dart'; import 'package:wolf_3d_entities/src/entities/enemies/officer.dart'; import 'package:wolf_3d_entities/src/entities/enemies/ss.dart'; import 'package:wolf_3d_entities/src/entity.dart'; -enum EnemyAnimation { idle, walking, attacking, pain, dying, dead } - -enum EnemyType { - guard(staticId: 23, patrolId: 108, spriteBaseIdx: 50), - officer(staticId: 26, patrolId: 126, spriteBaseIdx: 238), - ss(staticId: 29, patrolId: 144, spriteBaseIdx: 138), - dog(staticId: 32, patrolId: 162, spriteBaseIdx: 99), - mutant(staticId: 35, patrolId: 180, spriteBaseIdx: 187); - - final int staticId; - final int patrolId; - final int spriteBaseIdx; - - const EnemyType({ - required this.staticId, - required this.patrolId, - required this.spriteBaseIdx, - }); - - /// Checks if the ID belongs to this enemy type range - bool claimsMapId(int id) { - // Static enemies span 3 IDs (Easy, Medium, Hard) - bool isStatic = id >= staticId && id < staticId + 3; - // Patrolling enemies span 18 IDs per type - bool isPatrol = id >= patrolId && id < patrolId + 18; - return isStatic || isPatrol; - } - - static EnemyType? fromMapId(int id) { - for (final type in EnemyType.values) { - if (type.claimsMapId(id)) return type; - } - return null; - } - - bool claimsSpriteIndex(int index) { - return switch (this) { - // Walk, Action, & Death: 50-98 - EnemyType.guard => index >= 50 && index <= 98, - // Walk, Action, & Death: 99-137 - EnemyType.dog => index >= 99 && index <= 137, - // Walk, Action, & Death: 138-186 - EnemyType.ss => index >= 138 && index <= 186, - // Walk, Action, & Death: 187-237 - EnemyType.mutant => index >= 187 && index <= 237, - // Walk, Action, & Death: 238-287 - EnemyType.officer => index >= 238 && index <= 287, - }; - } - - EnemyAnimation? getAnimationFromSprite(int spriteIndex) { - if (!claimsSpriteIndex(spriteIndex)) return null; - - int offset = spriteIndex - spriteBaseIdx; - - if (offset >= 0 && offset <= 7) return EnemyAnimation.idle; - - return switch (this) { - EnemyType.guard || EnemyType.ss => switch (offset) { - >= 8 && <= 39 => EnemyAnimation.walking, - >= 40 && <= 42 => EnemyAnimation.dying, - 43 => EnemyAnimation.pain, - >= 44 && <= 46 => EnemyAnimation.attacking, - _ => EnemyAnimation.dead, - }, - - EnemyType.officer || EnemyType.mutant => switch (offset) { - >= 8 && <= 39 => EnemyAnimation.walking, - // All humanoids share 3 dying frames - >= 40 && <= 42 => EnemyAnimation.dying, - 43 => EnemyAnimation.pain, - // Officers/Mutants only have 2 attack frames - >= 44 && <= 45 => EnemyAnimation.attacking, - _ => EnemyAnimation.dead, - }, - - EnemyType.dog => switch (offset) { - >= 8 && <= 31 => EnemyAnimation.walking, - >= 32 && <= 34 => EnemyAnimation.attacking, - >= 35 && <= 37 => EnemyAnimation.dying, - _ => EnemyAnimation.dead, - }, - }; - } - - int getSpriteFromAnimation({ - required EnemyAnimation animation, - required int elapsedMs, - required int lastActionTime, - double angleDiff = 0, - int? walkFrameOverride, - }) { - 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 => - spriteBaseIdx + - (time < 150 - ? 44 - : time < 300 - ? 45 - : 46), - EnemyType.officer || - EnemyType.mutant => spriteBaseIdx + (time < 200 ? 44 : 45), - EnemyType.dog => spriteBaseIdx + (time < 150 ? 32 : 33), - }; - }(), - - EnemyAnimation.pain => - spriteBaseIdx + - switch (this) { - EnemyType.dog => 32, - _ => 43, - }, - - EnemyAnimation.dying => () { - int frame = (elapsedMs - lastActionTime) ~/ 150; - int dyingStart = switch (this) { - EnemyType.dog => 35, - _ => 40, - }; - return spriteBaseIdx + dyingStart + (frame.clamp(0, 2)); - }(), - - EnemyAnimation.dead => - spriteBaseIdx + - switch (this) { - EnemyType.dog => 38, - EnemyType.officer || EnemyType.mutant => 46, - _ => 48, - }, - }; - } -} - abstract class Enemy extends Entity { Enemy({ required super.x, @@ -340,33 +194,41 @@ abstract class Enemy extends Entity { Difficulty difficulty, { bool isSharewareMode = false, }) { - // ID 124 (dead guard) and 125 (dead aardwolf) fall inside Guard's patrol - // range (108–125) but are decorative bodies, not live actors. if (objId == MapObject.deadGuard || objId == MapObject.deadAardwolf) { return null; } - if (objId >= MapObject.playerNorth && objId <= MapObject.playerWest) { return null; } - // 2. I use the utility to check if this enemy is allowed on this difficulty - if (!MapObject.isDifficultyAllowed(objId, difficulty)) return null; - - // 3. I check if I even know what this enemy is final type = EnemyType.fromMapId(objId); if (type == null) return null; - bool isPatrolling = objId >= type.patrolId; - double spawnAngle = MapObject.getAngle(objId); + final mapData = type.mapData; - // 2. Return the specific instance + // 1. Validate Difficulty and Determine State + 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 + } else if (mapData.isAmbushForDifficulty(objId, difficulty)) { + spawnState = EntityState.ambush; // Stays perfectly still until alerted + } else { + // If the ID belongs to this enemy but NOT this difficulty, skip spawning! + return null; + } + + // 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), 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; + }..state = spawnState; } } diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_animation.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_animation.dart new file mode 100644 index 0000000..d159f0e --- /dev/null +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_animation.dart @@ -0,0 +1 @@ +enum EnemyAnimation { idle, walking, attacking, pain, dying, dead } 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 new file mode 100644 index 0000000..70fb96b --- /dev/null +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/enemy_type.dart @@ -0,0 +1,169 @@ +import 'dart:math' as math; + +import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; + +/// Maps all possible animation states to their specific frame ranges. +class EnemyAnimationMap { + final SpriteFrameRange idle; + final SpriteFrameRange walking; + final SpriteFrameRange attacking; + final SpriteFrameRange pain; + final SpriteFrameRange dying; + final SpriteFrameRange dead; + + const EnemyAnimationMap({ + required this.idle, + required this.walking, + required this.attacking, + required this.pain, + required this.dying, + required this.dead, + }); + + EnemyAnimation? getAnimation(int spriteIndex) { + if (idle.contains(spriteIndex)) return EnemyAnimation.idle; + if (walking.contains(spriteIndex)) return EnemyAnimation.walking; + if (attacking.contains(spriteIndex)) return EnemyAnimation.attacking; + if (pain.contains(spriteIndex)) return EnemyAnimation.pain; + if (dying.contains(spriteIndex)) return EnemyAnimation.dying; + if (dead.contains(spriteIndex)) return EnemyAnimation.dead; + return null; + } + + SpriteFrameRange getRange(EnemyAnimation animation) { + return switch (animation) { + EnemyAnimation.idle => idle, + EnemyAnimation.walking => walking, + EnemyAnimation.attacking => attacking, + EnemyAnimation.pain => pain, + EnemyAnimation.dying => dying, + EnemyAnimation.dead => dead, + }; + } +} + +enum EnemyType { + guard( + mapData: EnemyMapData(baseStaticId: 23, basePatrolId: 108), + animations: EnemyAnimationMap( + idle: SpriteFrameRange(50, 57), + walking: SpriteFrameRange(58, 89), + dying: SpriteFrameRange(90, 93), + pain: SpriteFrameRange(94, 94), + dead: SpriteFrameRange(95, 95), + attacking: SpriteFrameRange(96, 98), + ), + ), + dog( + mapData: EnemyMapData(baseStaticId: 26, basePatrolId: 126), + // Translated from your old offset logic so the game doesn't break + 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 + dying: SpriteFrameRange(134, 136), + dead: SpriteFrameRange(137, 137), + ), + ), + ss( + mapData: EnemyMapData(baseStaticId: 29, basePatrolId: 144), + animations: EnemyAnimationMap( + idle: SpriteFrameRange(138, 145), + walking: SpriteFrameRange(146, 177), + attacking: SpriteFrameRange(178, 179), + pain: SpriteFrameRange(180, 180), + dying: SpriteFrameRange(181, 182), + dead: SpriteFrameRange(183, 183), + ), + ), + mutant( + mapData: EnemyMapData(baseStaticId: 32, basePatrolId: 162), + animations: EnemyAnimationMap( + idle: SpriteFrameRange(187, 194), + walking: SpriteFrameRange(195, 226), + attacking: SpriteFrameRange(227, 228), + pain: SpriteFrameRange(229, 229), + dying: SpriteFrameRange(230, 231), + dead: SpriteFrameRange(232, 232), + ), + ), + officer( + mapData: EnemyMapData(baseStaticId: 35, basePatrolId: 180), + animations: EnemyAnimationMap( + idle: SpriteFrameRange(238, 245), + walking: SpriteFrameRange(246, 277), + attacking: SpriteFrameRange(278, 279), + pain: SpriteFrameRange(280, 280), + dying: SpriteFrameRange(281, 282), + dead: SpriteFrameRange(283, 283), + ), + ); + + final EnemyMapData mapData; + final EnemyAnimationMap animations; + + const EnemyType({required this.mapData, required this.animations}); + + static EnemyType? fromMapId(int id) { + for (final type in EnemyType.values) { + if (type.mapData.claimsId(id)) return type; + } + return null; + } + + bool claimsSpriteIndex(int index) => animations.getAnimation(index) != null; + + EnemyAnimation? getAnimationFromSprite(int spriteIndex) { + return animations.getAnimation(spriteIndex); + } + + int getSpriteFromAnimation({ + required EnemyAnimation animation, + required int elapsedMs, + required int lastActionTime, + double angleDiff = 0, + int? walkFrameOverride, + }) { + final range = animations.getRange(animation); + + // Calculates which of the 8 directions the enemy is facing relative to player + int octant = ((angleDiff + (math.pi / 8)) / (math.pi / 4)).floor() % 8; + if (octant < 0) octant += 8; + + return switch (animation) { + EnemyAnimation.idle => range.start + octant, + + EnemyAnimation.walking => () { + // Automatically calculates frames per angle (e.g. 32 frames / 8 angles = 4 frames) + int framesPerAngle = range.length ~/ 8; + if (framesPerAngle < 1) framesPerAngle = 1; // Failsafe + + int frame = walkFrameOverride ?? (elapsedMs ~/ 150) % framesPerAngle; + return range.start + (frame * 8) + octant; + }(), + + EnemyAnimation.attacking => () { + int time = elapsedMs - lastActionTime; + // Progresses through attack frames based on time, clamping at the last frame + int mappedFrame = (time ~/ 150).clamp(0, range.length - 1); + return range.start + mappedFrame; + }(), + + EnemyAnimation.pain => range.start, + + EnemyAnimation.dying => () { + int time = elapsedMs - lastActionTime; + // Progresses through death frames, staying on the final frame + int mappedFrame = (time ~/ 150).clamp(0, range.length - 1); + return range.start + mappedFrame; + }(), + + EnemyAnimation.dead => range.start, + }; + } +} diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/guard.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/guard.dart index ee6f0fb..794f49a 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/guard.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/guard.dart @@ -2,6 +2,8 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/enemy.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entity.dart'; class Guard extends Enemy { @@ -15,7 +17,7 @@ class Guard extends Enemy { required super.y, required super.angle, required super.mapId, - }) : super(spriteIndex: type.spriteBaseIdx, state: EntityState.idle); + }) : super(spriteIndex: type.animations.idle.start, state: EntityState.idle); @override ({Coordinate2D movement, double newAngle}) update({ @@ -41,10 +43,6 @@ class Guard extends Enemy { newAngle = angleToPlayer; } - if (state != EntityState.idle && state != EntityState.dead) { - newAngle = angleToPlayer; - } - // Calculate angle diff for the octant logic double diff = angleToPlayer - newAngle; while (diff <= -math.pi) { diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/mutant.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/mutant.dart index 0c782ea..b8eb318 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/mutant.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/mutant.dart @@ -2,6 +2,8 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/enemy.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entity.dart'; class Mutant extends Enemy { @@ -15,7 +17,7 @@ class Mutant extends Enemy { required super.y, required super.angle, required super.mapId, - }) : super(spriteIndex: type.spriteBaseIdx, state: EntityState.idle) { + }) : super(spriteIndex: type.animations.idle.start, state: EntityState.idle) { health = 45; damage = 10; } @@ -40,7 +42,7 @@ class Mutant extends Enemy { double distance = position.distanceTo(playerPosition); double angleToPlayer = position.angleTo(playerPosition); - if (state != EntityState.idle && state != EntityState.dead) { + if (isAlerted && state != EntityState.dead) { newAngle = angleToPlayer; } @@ -69,10 +71,6 @@ class Mutant extends Enemy { angleDiff: diff, ); - if (isAlerted && state != EntityState.dead) { - newAngle = angleToPlayer; - } - if (state == EntityState.patrolling) { // FIX 2: Move along patrol angle if unalerted, chase if alerted if (!isAlerted || distance > 0.8) { diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/officer.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/officer.dart index d805f9d..0637d2c 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/officer.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/officer.dart @@ -2,21 +2,22 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/enemy.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entity.dart'; class Officer extends Enemy { static const double speed = 0.055; bool _hasFiredThisCycle = false; + static EnemyType get type => EnemyType.officer; + Officer({ required super.x, required super.y, required super.angle, required super.mapId, - }) : super( - spriteIndex: EnemyType.officer.spriteBaseIdx, - state: EntityState.idle, - ) { + }) : super(spriteIndex: type.animations.idle.start, state: EntityState.idle) { health = 50; damage = 15; } @@ -41,7 +42,7 @@ class Officer extends Enemy { double distance = position.distanceTo(playerPosition); double angleToPlayer = position.angleTo(playerPosition); - if (state != EntityState.idle && state != EntityState.dead) { + if (isAlerted && state != EntityState.dead) { newAngle = angleToPlayer; } @@ -62,17 +63,13 @@ class Officer extends Enemy { _ => EnemyAnimation.idle, }; - spriteIndex = EnemyType.officer.getSpriteFromAnimation( + spriteIndex = type.getSpriteFromAnimation( animation: currentAnim, elapsedMs: elapsedMs, lastActionTime: lastActionTime, angleDiff: diff, ); - if (isAlerted && state != EntityState.dead) { - newAngle = angleToPlayer; - } - if (state == EntityState.patrolling) { // FIX 2: Move along patrol angle if unalerted, chase if alerted if (!isAlerted || distance > 0.8) { diff --git a/packages/wolf_3d_entities/lib/src/entities/enemies/ss.dart b/packages/wolf_3d_entities/lib/src/entities/enemies/ss.dart index 26e1854..13ba3ca 100644 --- a/packages/wolf_3d_entities/lib/src/entities/enemies/ss.dart +++ b/packages/wolf_3d_entities/lib/src/entities/enemies/ss.dart @@ -2,6 +2,8 @@ import 'dart:math' as math; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_entities/src/entities/enemies/enemy.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart'; +import 'package:wolf_3d_entities/src/entities/enemies/enemy_type.dart'; import 'package:wolf_3d_entities/src/entity.dart'; class SS extends Enemy { @@ -15,7 +17,7 @@ class SS extends Enemy { required super.y, required super.angle, required super.mapId, - }) : super(spriteIndex: EnemyType.ss.spriteBaseIdx, state: EntityState.idle) { + }) : super(spriteIndex: type.animations.idle.start, state: EntityState.idle) { health = 100; damage = 20; } @@ -40,7 +42,7 @@ class SS extends Enemy { double distance = position.distanceTo(playerPosition); double angleToPlayer = position.angleTo(playerPosition); - if (state != EntityState.idle && state != EntityState.dead) { + if (isAlerted && state != EntityState.dead) { newAngle = angleToPlayer; } @@ -69,10 +71,6 @@ class SS extends Enemy { angleDiff: diff, ); - if (isAlerted && state != EntityState.dead) { - newAngle = angleToPlayer; - } - if (state == EntityState.patrolling) { // FIX 2: Move along patrol angle if unalerted, chase if alerted if (!isAlerted || distance > 0.8) { diff --git a/packages/wolf_3d_entities/lib/src/entity.dart b/packages/wolf_3d_entities/lib/src/entity.dart index 9bfd577..b0c1571 100644 --- a/packages/wolf_3d_entities/lib/src/entity.dart +++ b/packages/wolf_3d_entities/lib/src/entity.dart @@ -1,6 +1,6 @@ import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; -enum EntityState { staticObj, idle, patrolling, attacking, pain, dead } +enum EntityState { staticObj, ambush, idle, patrolling, attacking, pain, dead } abstract class Entity { double x; diff --git a/packages/wolf_3d_entities/lib/wolf_3d_entities.dart b/packages/wolf_3d_entities/lib/wolf_3d_entities.dart index 7862f13..174294b 100644 --- a/packages/wolf_3d_entities/lib/wolf_3d_entities.dart +++ b/packages/wolf_3d_entities/lib/wolf_3d_entities.dart @@ -9,6 +9,8 @@ export 'src/entities/door.dart'; export 'src/entities/enemies/bosses/hans_grosse.dart'; export 'src/entities/enemies/dog.dart'; export 'src/entities/enemies/enemy.dart'; +export 'src/entities/enemies/enemy_animation.dart'; +export 'src/entities/enemies/enemy_type.dart'; export 'src/entities/enemies/guard.dart'; export 'src/entities/enemies/mutant.dart'; export 'src/entities/enemies/officer.dart';