Trying to figure out issue with sprites loading improperly. Broken, still.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-14 23:20:03 +01:00
parent 1ec891d9a0
commit 8ea7642c8b
12 changed files with 242 additions and 320 deletions

View File

@@ -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;
}
}