Added tests for validating enemy sprite ranges
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -59,7 +59,10 @@ class SpriteGallery extends StatelessWidget {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: WolfAssetPainter.sprite(sprites[index]),
|
child: AspectRatio(
|
||||||
|
aspectRatio: 4 / 3,
|
||||||
|
child: WolfAssetPainter.sprite(sprites[index]),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:wolf_3d_dart/src/data_types/sprite_frame_range.dart';
|
import 'package:wolf_3d_dart/src/data_types/sprite_frame_range.dart';
|
||||||
import 'package:wolf_3d_dart/src/entities/entities/enemies/enemy_type.dart';
|
|
||||||
|
|
||||||
enum EnemyAnimation { idle, walking, attacking, pain, dying, dead }
|
enum EnemyAnimation { idle, walking, attacking, pain, dying, dead }
|
||||||
|
|
||||||
@@ -90,33 +89,12 @@ class EnemyAnimationMap {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validateEnemyAnimations() {
|
Map<EnemyAnimation, SpriteFrameRange> get allRanges => {
|
||||||
bool hasErrors = false;
|
EnemyAnimation.idle: idle,
|
||||||
|
EnemyAnimation.walking: walking,
|
||||||
for (final enemy in EnemyType.values) {
|
EnemyAnimation.attacking: attacking,
|
||||||
// 1. Check for internal overlaps (e.g., Guard walking overlaps Guard attacking)
|
EnemyAnimation.pain: pain,
|
||||||
if (enemy.animations.hasInternalOverlaps()) {
|
EnemyAnimation.dying: dying,
|
||||||
print(
|
EnemyAnimation.dead: dead,
|
||||||
'❌ ERROR: ${enemy.name} has overlapping internal animation states!',
|
};
|
||||||
);
|
|
||||||
hasErrors = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Check for external overlaps (e.g., Guard sprites overlap SS sprites)
|
|
||||||
for (final otherEnemy in EnemyType.values) {
|
|
||||||
if (enemy != otherEnemy && enemy.sharesFramesWith(otherEnemy)) {
|
|
||||||
print(
|
|
||||||
'❌ ERROR: ${enemy.name} shares sprite frames with ${otherEnemy.name}!',
|
|
||||||
);
|
|
||||||
hasErrors = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasErrors) {
|
|
||||||
print(
|
|
||||||
'✅ All enemy animations validated successfully! No overlaps found.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,22 +172,4 @@ enum EnemyType {
|
|||||||
EnemyAnimation.dead => range.start,
|
EnemyAnimation.dead => range.start,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if this enemy shares any sprite frames with [other].
|
|
||||||
bool sharesFramesWith(EnemyType other) {
|
|
||||||
return animations.overlapsWith(other.animations);
|
|
||||||
|
|
||||||
// * Usage example:
|
|
||||||
// void checkEnemyOverlaps() {
|
|
||||||
// for (final enemyA in EnemyType.values) {
|
|
||||||
// for (final enemyB in EnemyType.values) {
|
|
||||||
// if (enemyA != enemyB && enemyA.sharesFramesWith(enemyB)) {
|
|
||||||
// print(
|
|
||||||
// 'Warning: ${enemyA.name} and ${enemyB.name} have overlapping sprites!',
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:wolf_3d_dart/src/entities/entities/enemies/enemy_type.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('Enemy Sprite Range Validation', () {
|
||||||
|
test('No enemy animations should have overlapping sprite indices', () {
|
||||||
|
final allEnemies = EnemyType.values;
|
||||||
|
final overlaps = <String>[];
|
||||||
|
|
||||||
|
for (int i = 0; i < allEnemies.length; i++) {
|
||||||
|
final enemyA = allEnemies[i];
|
||||||
|
final animationsA = enemyA.animations.allRanges;
|
||||||
|
|
||||||
|
for (int j = i; j < allEnemies.length; j++) {
|
||||||
|
final enemyB = allEnemies[j];
|
||||||
|
final animationsB = enemyB.animations.allRanges;
|
||||||
|
|
||||||
|
animationsA.forEach((animA, rangeA) {
|
||||||
|
animationsB.forEach((animB, rangeB) {
|
||||||
|
// Skip if we are comparing the exact same animation on the same enemy
|
||||||
|
if (enemyA == enemyB && animA == animB) return;
|
||||||
|
|
||||||
|
if (rangeA.overlaps(rangeB)) {
|
||||||
|
// Determine the specific frames that are clashing
|
||||||
|
final start = math.max(rangeA.start, rangeB.start);
|
||||||
|
final end = math.min(rangeA.end, rangeB.end);
|
||||||
|
final clashingFrames = <int>[];
|
||||||
|
|
||||||
|
for (int f = start; f <= end; f++) {
|
||||||
|
if (rangeA.contains(f) && rangeB.contains(f)) {
|
||||||
|
clashingFrames.add(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clashingFrames.isNotEmpty) {
|
||||||
|
overlaps.add(
|
||||||
|
'${enemyA.name}.${animA.name} overlaps ${enemyB.name}.${animB.name} on frames: $clashingFrames',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the list of overlaps is empty
|
||||||
|
expect(
|
||||||
|
overlaps,
|
||||||
|
isEmpty,
|
||||||
|
reason: 'Detected sprite index collisions:\n${overlaps.join('\n')}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user