WIP moving difficulty selection to engine
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
167
packages/wolf_3d_dart/lib/wolf_3d_menu.dart
Normal file
167
packages/wolf_3d_dart/lib/wolf_3d_menu.dart
Normal file
@@ -0,0 +1,167 @@
|
||||
/// Shared menu helpers for Wolf3D hosts.
|
||||
library;
|
||||
|
||||
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
|
||||
|
||||
/// Known VGA picture indexes used by the original Wolf3D control-panel menus.
|
||||
///
|
||||
/// Values below are picture-table indexes (not raw chunk ids).
|
||||
/// For example, `C_CONTROLPIC` is chunk 26 in `GFXV_WL6.H`, so its picture
|
||||
/// index is `26 - STARTPICS(3) = 23`.
|
||||
abstract class WolfMenuPic {
|
||||
static const int hBj = 0; // H_BJPIC
|
||||
static const int hTopWindow = 3; // H_TOPWINDOWPIC
|
||||
static const int cOptions = 7; // C_OPTIONSPIC
|
||||
static const int cCursor1 = 8; // C_CURSOR1PIC
|
||||
static const int cCursor2 = 9; // C_CURSOR2PIC
|
||||
static const int cNotSelected = 10; // C_NOTSELECTEDPIC
|
||||
static const int cSelected = 11; // C_SELECTEDPIC
|
||||
static const int cBabyMode = 16; // C_BABYMODEPIC
|
||||
static const int cEasy = 17; // C_EASYPIC
|
||||
static const int cNormal = 18; // C_NORMALPIC
|
||||
static const int cHard = 19; // C_HARDPIC
|
||||
static const int cControl = 23; // C_CONTROLPIC
|
||||
static const int cEpisode1 = 27; // C_EPISODE1PIC
|
||||
static const int cEpisode2 = 28; // C_EPISODE2PIC
|
||||
static const int cEpisode3 = 29; // C_EPISODE3PIC
|
||||
static const int cEpisode4 = 30; // C_EPISODE4PIC
|
||||
static const int cEpisode5 = 31; // C_EPISODE5PIC
|
||||
static const int cEpisode6 = 32; // C_EPISODE6PIC
|
||||
static const int statusBar = 83; // STATUSBARPIC
|
||||
static const int title = 84; // TITLEPIC
|
||||
static const int pg13 = 85; // PG13PIC
|
||||
static const int credits = 86; // CREDITSPIC
|
||||
static const int highScores = 87; // HIGHSCORESPIC
|
||||
|
||||
static const List<int> episodePics = [
|
||||
cEpisode1,
|
||||
cEpisode2,
|
||||
cEpisode3,
|
||||
cEpisode4,
|
||||
cEpisode5,
|
||||
cEpisode6,
|
||||
];
|
||||
}
|
||||
|
||||
/// Structured accessors for classic Wolf3D menu art.
|
||||
class WolfClassicMenuArt {
|
||||
final WolfensteinData data;
|
||||
|
||||
WolfClassicMenuArt(this.data);
|
||||
|
||||
int? _resolvedIndexOffset;
|
||||
|
||||
VgaImage? get controlBackground {
|
||||
final preferred = mappedPic(WolfMenuPic.cControl);
|
||||
if (_looksLikeMenuBackdrop(preferred)) {
|
||||
return preferred;
|
||||
}
|
||||
|
||||
// Older data layouts may shift/control-panel art around nearby indices.
|
||||
for (int delta = -4; delta <= 4; delta++) {
|
||||
final candidate = mappedPic(WolfMenuPic.cControl + delta);
|
||||
if (_looksLikeMenuBackdrop(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return preferred;
|
||||
}
|
||||
|
||||
VgaImage? get title => mappedPic(WolfMenuPic.title);
|
||||
|
||||
VgaImage? get heading => mappedPic(WolfMenuPic.hTopWindow);
|
||||
|
||||
VgaImage? get selectedMarker => mappedPic(WolfMenuPic.cSelected);
|
||||
|
||||
VgaImage? get unselectedMarker => mappedPic(WolfMenuPic.cNotSelected);
|
||||
|
||||
VgaImage? get optionsLabel => mappedPic(WolfMenuPic.cOptions);
|
||||
|
||||
VgaImage? get credits => mappedPic(WolfMenuPic.credits);
|
||||
|
||||
VgaImage? episodeOption(int episodeIndex) {
|
||||
if (episodeIndex < 0 || episodeIndex >= WolfMenuPic.episodePics.length) {
|
||||
return null;
|
||||
}
|
||||
return mappedPic(WolfMenuPic.episodePics[episodeIndex]);
|
||||
}
|
||||
|
||||
VgaImage? difficultyOption(Difficulty difficulty) {
|
||||
switch (difficulty) {
|
||||
case Difficulty.baby:
|
||||
return mappedPic(WolfMenuPic.cBabyMode);
|
||||
case Difficulty.easy:
|
||||
return mappedPic(WolfMenuPic.cEasy);
|
||||
case Difficulty.medium:
|
||||
return mappedPic(WolfMenuPic.cNormal);
|
||||
case Difficulty.hard:
|
||||
return mappedPic(WolfMenuPic.cHard);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [index] after applying a detected version/layout offset.
|
||||
VgaImage? mappedPic(int index) {
|
||||
return pic(index + _indexOffset);
|
||||
}
|
||||
|
||||
int get _indexOffset {
|
||||
if (_resolvedIndexOffset != null) {
|
||||
return _resolvedIndexOffset!;
|
||||
}
|
||||
|
||||
// Retail and shareware generally place STATUSBAR/TITLE/PG13/CREDITS as a
|
||||
// contiguous block. If files are from a different release, infer a shift.
|
||||
for (int i = 0; i < data.vgaImages.length - 3; i++) {
|
||||
final status = data.vgaImages[i];
|
||||
if (!_looksLikeStatusBar(status)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final title = data.vgaImages[i + 1];
|
||||
final pg13 = data.vgaImages[i + 2];
|
||||
final credits = data.vgaImages[i + 3];
|
||||
if (_looksLikeFullScreen(title) &&
|
||||
_looksLikeFullScreen(pg13) &&
|
||||
_looksLikeFullScreen(credits)) {
|
||||
_resolvedIndexOffset = i - WolfMenuPic.statusBar;
|
||||
return _resolvedIndexOffset!;
|
||||
}
|
||||
}
|
||||
|
||||
_resolvedIndexOffset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool _looksLikeStatusBar(VgaImage image) {
|
||||
return image.width >= 280 && image.height >= 24 && image.height <= 64;
|
||||
}
|
||||
|
||||
bool _looksLikeFullScreen(VgaImage image) {
|
||||
return image.width >= 280 && image.height >= 140;
|
||||
}
|
||||
|
||||
bool _looksLikeMenuBackdrop(VgaImage? image) {
|
||||
if (image == null) {
|
||||
return false;
|
||||
}
|
||||
return image.width >= 180 && image.height >= 100;
|
||||
}
|
||||
|
||||
VgaImage? pic(int index) {
|
||||
if (index < 0 || index >= data.vgaImages.length) {
|
||||
return null;
|
||||
}
|
||||
final image = data.vgaImages[index];
|
||||
|
||||
// Ignore known gameplay HUD art in menu composition.
|
||||
if (index == WolfMenuPic.statusBar + _indexOffset) {
|
||||
return null;
|
||||
}
|
||||
if (image.width <= 0 || image.height <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user