Load menu and level audio dynamically
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -69,11 +69,15 @@ class _WolfRendererState extends State<WolfRenderer>
|
|||||||
// Grab the specific level from the singleton
|
// Grab the specific level from the singleton
|
||||||
_currentLevel = Wolf3d.I.levels[mapIndex];
|
_currentLevel = Wolf3d.I.levels[mapIndex];
|
||||||
|
|
||||||
|
// Play the exact track id Software intended for this level!
|
||||||
|
Wolf3d.I.audio.playLevelMusic(_currentLevel);
|
||||||
|
|
||||||
// TODO: Initialize player position, spawn enemies based on difficulty, etc.
|
// TODO: Initialize player position, spawn enemies based on difficulty, etc.
|
||||||
debugPrint("Loaded Level: ${_currentLevel.name}");
|
debugPrint("Loaded Level: ${_currentLevel.name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onLevelCompleted() {
|
void _onLevelCompleted() {
|
||||||
|
Wolf3d.I.audio.stopMusic();
|
||||||
// When the player hits the elevator switch, advance the map
|
// When the player hits the elevator switch, advance the map
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentMapIndex++;
|
_currentMapIndex++;
|
||||||
|
|||||||
@@ -22,16 +22,13 @@ class _EpisodeScreenState extends State<EpisodeScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (Wolf3d.I.music.isNotEmpty) {
|
Wolf3d.I.audio.playMenuMusic();
|
||||||
Wolf3d.I.audio.playMusic(Wolf3d.I.music.first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectEpisode(int index) {
|
void _selectEpisode(int index) {
|
||||||
Wolf3d.I.setActiveEpisode(index);
|
Wolf3d.I.setActiveEpisode(index);
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
// We pass the audio handles so the next screen can stop them when the game starts
|
|
||||||
builder: (context) => DifficultyScreen(),
|
builder: (context) => DifficultyScreen(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -39,7 +36,6 @@ class _EpisodeScreenState extends State<EpisodeScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Determine how many episodes are available (10 levels per episode)
|
|
||||||
final int numberOfEpisodes = (Wolf3d.I.levels.length / 10).floor().clamp(
|
final int numberOfEpisodes = (Wolf3d.I.levels.length / 10).floor().clamp(
|
||||||
1,
|
1,
|
||||||
6,
|
6,
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class Wolf3d {
|
|||||||
// --- Actions ---
|
// --- Actions ---
|
||||||
void setActiveGame(WolfensteinData game) {
|
void setActiveGame(WolfensteinData game) {
|
||||||
_activeGame = game;
|
_activeGame = game;
|
||||||
|
audio.activeGame = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the engine by loading available game data.
|
/// Initializes the engine by loading available game data.
|
||||||
|
|||||||
@@ -4,6 +4,20 @@ import 'dart:typed_data';
|
|||||||
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
||||||
|
|
||||||
abstract class WLParser {
|
abstract class WLParser {
|
||||||
|
// --- Original Song Lookup Tables ---
|
||||||
|
static const List<int> _sharewareMusicMap = [
|
||||||
|
2, 3, 4, 5, 2, 3, 4, 5, 6, 7, // Episode 1
|
||||||
|
];
|
||||||
|
|
||||||
|
static const List<int> _retailMusicMap = [
|
||||||
|
2, 3, 4, 5, 2, 3, 4, 5, 6, 7, // Ep 1
|
||||||
|
8, 9, 10, 11, 8, 9, 11, 10, 6, 12, // Ep 2
|
||||||
|
13, 14, 15, 16, 13, 14, 15, 16, 17, 18, // Ep 3
|
||||||
|
2, 3, 4, 5, 2, 3, 4, 5, 6, 7, // Ep 4
|
||||||
|
8, 9, 10, 11, 8, 9, 11, 10, 6, 12, // Ep 5
|
||||||
|
13, 14, 15, 16, 13, 14, 15, 16, 17, 19, // Ep 6
|
||||||
|
];
|
||||||
|
|
||||||
/// Asynchronously discovers the game version and loads all necessary files.
|
/// Asynchronously discovers the game version and loads all necessary files.
|
||||||
/// Provide a [fileFetcher] callback (e.g., Flutter's rootBundle.load) that
|
/// Provide a [fileFetcher] callback (e.g., Flutter's rootBundle.load) that
|
||||||
/// takes a filename and returns its ByteData.
|
/// takes a filename and returns its ByteData.
|
||||||
@@ -229,6 +243,10 @@ abstract class WLParser {
|
|||||||
bool isShareware = true,
|
bool isShareware = true,
|
||||||
}) {
|
}) {
|
||||||
List<WolfLevel> levels = [];
|
List<WolfLevel> levels = [];
|
||||||
|
|
||||||
|
// 1. Select the correct map based on the version
|
||||||
|
final activeMusicMap = isShareware ? _sharewareMusicMap : _retailMusicMap;
|
||||||
|
|
||||||
int rlewTag = mapHead.getUint16(0, Endian.little);
|
int rlewTag = mapHead.getUint16(0, Endian.little);
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
@@ -279,11 +297,20 @@ abstract class WLParser {
|
|||||||
objectGrid.add(objectRow);
|
objectGrid.add(objectRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine music track index.
|
||||||
|
// We use 'i' because it represents the absolute map slot (e.g. Map 12).
|
||||||
|
// If a map exists past the bounds of our lookup table (custom maps),
|
||||||
|
// we wrap around safely.
|
||||||
|
int trackIndex = (i < activeMusicMap.length)
|
||||||
|
? activeMusicMap[i]
|
||||||
|
: activeMusicMap[i % activeMusicMap.length];
|
||||||
|
|
||||||
levels.add(
|
levels.add(
|
||||||
WolfLevel(
|
WolfLevel(
|
||||||
name: name,
|
name: name,
|
||||||
wallGrid: wallGrid,
|
wallGrid: wallGrid,
|
||||||
objectGrid: objectGrid,
|
objectGrid: objectGrid,
|
||||||
|
musicIndex: trackIndex,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,3 +49,5 @@ class ImfMusic {
|
|||||||
return ImfMusic(instructions);
|
return ImfMusic(instructions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef WolfMusicMap = List<int>;
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ class WolfLevel {
|
|||||||
final String name;
|
final String name;
|
||||||
final Sprite wallGrid;
|
final Sprite wallGrid;
|
||||||
final Sprite objectGrid;
|
final Sprite objectGrid;
|
||||||
|
final int musicIndex;
|
||||||
|
|
||||||
const WolfLevel({
|
const WolfLevel({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.wallGrid,
|
required this.wallGrid,
|
||||||
required this.objectGrid,
|
required this.objectGrid,
|
||||||
|
required this.musicIndex,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ library;
|
|||||||
export 'src/game_file.dart' show GameFile;
|
export 'src/game_file.dart' show GameFile;
|
||||||
export 'src/game_version.dart' show GameVersion;
|
export 'src/game_version.dart' show GameVersion;
|
||||||
export 'src/image.dart' show VgaImage;
|
export 'src/image.dart' show VgaImage;
|
||||||
export 'src/sound.dart' show PcmSound, AdLibSound, ImfMusic, ImfInstruction;
|
export 'src/sound.dart'
|
||||||
|
show PcmSound, AdLibSound, ImfMusic, ImfInstruction, WolfMusicMap;
|
||||||
export 'src/sprite.dart' hide Matrix;
|
export 'src/sprite.dart' hide Matrix;
|
||||||
export 'src/wolf_level.dart' show WolfLevel;
|
export 'src/wolf_level.dart' show WolfLevel;
|
||||||
export 'src/wolfenstein_data.dart' show WolfensteinData;
|
export 'src/wolfenstein_data.dart' show WolfensteinData;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class WolfAudio {
|
|||||||
AudioSource? _currentMusicSource;
|
AudioSource? _currentMusicSource;
|
||||||
SoundHandle? _currentMusicHandle;
|
SoundHandle? _currentMusicHandle;
|
||||||
|
|
||||||
|
WolfensteinData? activeGame;
|
||||||
|
|
||||||
/// Initializes the SoLoud audio engine.
|
/// Initializes the SoLoud audio engine.
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
if (_isInitialized) return;
|
if (_isInitialized) return;
|
||||||
@@ -89,4 +91,30 @@ class WolfAudio {
|
|||||||
SoLoud.instance.setPause(_currentMusicHandle!, false);
|
SoLoud.instance.setPause(_currentMusicHandle!, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> playMenuMusic() async {
|
||||||
|
final data = activeGame;
|
||||||
|
// We can't play if data isn't set or doesn't have enough tracks
|
||||||
|
if (data == null || data.music.length <= 1) return;
|
||||||
|
|
||||||
|
// Track 1 is the menu theme in both Shareware and Retail
|
||||||
|
await playMusic(data.music[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plays the specific track assigned to a WolfLevel.
|
||||||
|
Future<void> playLevelMusic(WolfLevel level) async {
|
||||||
|
final data = activeGame;
|
||||||
|
if (data == null || data.music.isEmpty) return;
|
||||||
|
|
||||||
|
final index = level.musicIndex;
|
||||||
|
|
||||||
|
if (index < data.music.length) {
|
||||||
|
// 3. FIXED THIS: Call your playMusic method
|
||||||
|
await playMusic(data.music[index]);
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
"WolfAudio: Warning - Track index $index out of bounds for level ${level.name}.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user