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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user