Fixed shareware sprites

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-15 20:12:59 +01:00
parent d4183beb3f
commit 173339af82
3 changed files with 34 additions and 21 deletions

View File

@@ -210,6 +210,9 @@ abstract class Enemy extends Entity {
final type = EnemyType.fromMapId(objId);
if (type == null) return null;
// Reject enemies that don't exist in the shareware data!
if (isSharewareMode && !type.existsInShareware) return null;
final mapData = type.mapData;
// ALL enemies have explicit directional angles!

View File

@@ -3,7 +3,6 @@ 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;
@@ -87,6 +86,7 @@ enum EnemyType {
dying: SpriteFrameRange(227, 230),
dead: SpriteFrameRange(232, 232),
),
existsInShareware: false,
),
officer(
mapData: EnemyMapData(MapObject.officerStart),
@@ -98,12 +98,18 @@ enum EnemyType {
dying: SpriteFrameRange(278, 281),
dead: SpriteFrameRange(283, 283),
),
existsInShareware: false,
);
final EnemyMapData mapData;
final EnemyAnimationMap animations;
final bool existsInShareware;
const EnemyType({required this.mapData, required this.animations});
const EnemyType({
required this.mapData,
required this.animations,
this.existsInShareware = true,
});
static EnemyType? fromMapId(int id) {
for (final type in EnemyType.values) {
@@ -112,10 +118,21 @@ enum EnemyType {
return null;
}
bool claimsSpriteIndex(int index) => animations.getAnimation(index) != null;
/// Returns the animations only if the enemy actually exists in the current version.
EnemyAnimationMap? getAnimations(bool isShareware) {
if (isShareware && !existsInShareware) return null;
return animations;
}
EnemyAnimation? getAnimationFromSprite(int spriteIndex) {
return animations.getAnimation(spriteIndex);
bool claimsSpriteIndex(int index, {bool isShareware = false}) {
return getAnimations(isShareware)?.getAnimation(index) != null;
}
EnemyAnimation? getAnimationFromSprite(
int spriteIndex, {
bool isShareware = false,
}) {
return getAnimations(isShareware)?.getAnimation(spriteIndex);
}
int getSpriteFromAnimation({
@@ -125,40 +142,33 @@ enum EnemyType {
double angleDiff = 0,
int? walkFrameOverride,
}) {
// We don't need to check isShareware here, because if the entity exists
// in the game world, it implicitly passed the version check during spawn.
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
if (framesPerAngle < 1) framesPerAngle = 1;
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,
};
}