Fixed shareware sprites
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
||||||
import 'package:wolf_3d_entities/wolf_3d_entities.dart';
|
import 'package:wolf_3d_entities/wolf_3d_entities.dart';
|
||||||
|
import 'package:wolf_3d_flutter/wolf_3d.dart';
|
||||||
import 'package:wolf_3d_renderer/color_palette.dart';
|
import 'package:wolf_3d_renderer/color_palette.dart';
|
||||||
|
|
||||||
class SpriteGallery extends StatelessWidget {
|
class SpriteGallery extends StatelessWidget {
|
||||||
@@ -8,6 +9,8 @@ class SpriteGallery extends StatelessWidget {
|
|||||||
|
|
||||||
const SpriteGallery({super.key, required this.sprites});
|
const SpriteGallery({super.key, required this.sprites});
|
||||||
|
|
||||||
|
bool get isShareware => Wolf3d.I.activeGame.version == GameVersion.shareware;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -25,10 +28,12 @@ class SpriteGallery extends StatelessWidget {
|
|||||||
// --- Check which enemy owns this sprite ---
|
// --- Check which enemy owns this sprite ---
|
||||||
String label = "Sprite Index: $index";
|
String label = "Sprite Index: $index";
|
||||||
for (final enemy in EnemyType.values) {
|
for (final enemy in EnemyType.values) {
|
||||||
if (enemy.claimsSpriteIndex(index)) {
|
if (enemy.claimsSpriteIndex(index, isShareware: isShareware)) {
|
||||||
final EnemyAnimation? animation = enemy.getAnimationFromSprite(
|
final EnemyAnimation? animation = enemy.getAnimationFromSprite(
|
||||||
index,
|
index,
|
||||||
|
isShareware: isShareware,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Appends the enum name (e.g., "guard", "dog")
|
// Appends the enum name (e.g., "guard", "dog")
|
||||||
label += "\n${enemy.name}";
|
label += "\n${enemy.name}";
|
||||||
|
|
||||||
@@ -37,11 +42,6 @@ class SpriteGallery extends StatelessWidget {
|
|||||||
label += "\n${animation.name}";
|
label += "\n${animation.name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the Map IDs for level editing reference
|
|
||||||
int staticBase = enemy.mapData.baseId;
|
|
||||||
|
|
||||||
label +=
|
|
||||||
"\nStat: $staticBase (E), ${staticBase + 1} (M), ${staticBase + 2} (H)";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,9 @@ abstract class Enemy extends Entity {
|
|||||||
final type = EnemyType.fromMapId(objId);
|
final type = EnemyType.fromMapId(objId);
|
||||||
if (type == null) return null;
|
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;
|
final mapData = type.mapData;
|
||||||
|
|
||||||
// ALL enemies have explicit directional angles!
|
// ALL enemies have explicit directional angles!
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'dart:math' as math;
|
|||||||
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
||||||
import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart';
|
import 'package:wolf_3d_entities/src/entities/enemies/enemy_animation.dart';
|
||||||
|
|
||||||
/// Maps all possible animation states to their specific frame ranges.
|
|
||||||
class EnemyAnimationMap {
|
class EnemyAnimationMap {
|
||||||
final SpriteFrameRange idle;
|
final SpriteFrameRange idle;
|
||||||
final SpriteFrameRange walking;
|
final SpriteFrameRange walking;
|
||||||
@@ -87,6 +86,7 @@ enum EnemyType {
|
|||||||
dying: SpriteFrameRange(227, 230),
|
dying: SpriteFrameRange(227, 230),
|
||||||
dead: SpriteFrameRange(232, 232),
|
dead: SpriteFrameRange(232, 232),
|
||||||
),
|
),
|
||||||
|
existsInShareware: false,
|
||||||
),
|
),
|
||||||
officer(
|
officer(
|
||||||
mapData: EnemyMapData(MapObject.officerStart),
|
mapData: EnemyMapData(MapObject.officerStart),
|
||||||
@@ -98,12 +98,18 @@ enum EnemyType {
|
|||||||
dying: SpriteFrameRange(278, 281),
|
dying: SpriteFrameRange(278, 281),
|
||||||
dead: SpriteFrameRange(283, 283),
|
dead: SpriteFrameRange(283, 283),
|
||||||
),
|
),
|
||||||
|
existsInShareware: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
final EnemyMapData mapData;
|
final EnemyMapData mapData;
|
||||||
final EnemyAnimationMap animations;
|
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) {
|
static EnemyType? fromMapId(int id) {
|
||||||
for (final type in EnemyType.values) {
|
for (final type in EnemyType.values) {
|
||||||
@@ -112,10 +118,21 @@ enum EnemyType {
|
|||||||
return null;
|
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) {
|
bool claimsSpriteIndex(int index, {bool isShareware = false}) {
|
||||||
return animations.getAnimation(spriteIndex);
|
return getAnimations(isShareware)?.getAnimation(index) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnemyAnimation? getAnimationFromSprite(
|
||||||
|
int spriteIndex, {
|
||||||
|
bool isShareware = false,
|
||||||
|
}) {
|
||||||
|
return getAnimations(isShareware)?.getAnimation(spriteIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getSpriteFromAnimation({
|
int getSpriteFromAnimation({
|
||||||
@@ -125,40 +142,33 @@ enum EnemyType {
|
|||||||
double angleDiff = 0,
|
double angleDiff = 0,
|
||||||
int? walkFrameOverride,
|
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);
|
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;
|
int octant = ((angleDiff + (math.pi / 8)) / (math.pi / 4)).floor() % 8;
|
||||||
if (octant < 0) octant += 8;
|
if (octant < 0) octant += 8;
|
||||||
|
|
||||||
return switch (animation) {
|
return switch (animation) {
|
||||||
EnemyAnimation.idle => range.start + octant,
|
EnemyAnimation.idle => range.start + octant,
|
||||||
|
|
||||||
EnemyAnimation.walking => () {
|
EnemyAnimation.walking => () {
|
||||||
// Automatically calculates frames per angle (e.g. 32 frames / 8 angles = 4 frames)
|
|
||||||
int framesPerAngle = range.length ~/ 8;
|
int framesPerAngle = range.length ~/ 8;
|
||||||
if (framesPerAngle < 1) framesPerAngle = 1; // Failsafe
|
if (framesPerAngle < 1) framesPerAngle = 1;
|
||||||
|
|
||||||
int frame = walkFrameOverride ?? (elapsedMs ~/ 150) % framesPerAngle;
|
int frame = walkFrameOverride ?? (elapsedMs ~/ 150) % framesPerAngle;
|
||||||
return range.start + (frame * 8) + octant;
|
return range.start + (frame * 8) + octant;
|
||||||
}(),
|
}(),
|
||||||
|
|
||||||
EnemyAnimation.attacking => () {
|
EnemyAnimation.attacking => () {
|
||||||
int time = elapsedMs - lastActionTime;
|
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);
|
int mappedFrame = (time ~/ 150).clamp(0, range.length - 1);
|
||||||
return range.start + mappedFrame;
|
return range.start + mappedFrame;
|
||||||
}(),
|
}(),
|
||||||
|
|
||||||
EnemyAnimation.pain => range.start,
|
EnemyAnimation.pain => range.start,
|
||||||
|
|
||||||
EnemyAnimation.dying => () {
|
EnemyAnimation.dying => () {
|
||||||
int time = elapsedMs - lastActionTime;
|
int time = elapsedMs - lastActionTime;
|
||||||
// Progresses through death frames, staying on the final frame
|
|
||||||
int mappedFrame = (time ~/ 150).clamp(0, range.length - 1);
|
int mappedFrame = (time ~/ 150).clamp(0, range.length - 1);
|
||||||
return range.start + mappedFrame;
|
return range.start + mappedFrame;
|
||||||
}(),
|
}(),
|
||||||
|
|
||||||
EnemyAnimation.dead => range.start,
|
EnemyAnimation.dead => range.start,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user