Handle exit elevators and secret levels
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -54,8 +54,9 @@ class _WolfRendererState extends State<WolfRenderer>
|
||||
|
||||
double damageFlashOpacity = 0.0;
|
||||
|
||||
late int _currentMapIndex;
|
||||
late WolfLevel _currentLevel;
|
||||
late int _currentEpisodeIndex;
|
||||
late int _currentLevelIndex;
|
||||
int? _returnLevelIndex;
|
||||
|
||||
List<Entity> entities = [];
|
||||
|
||||
@@ -65,52 +66,47 @@ class _WolfRendererState extends State<WolfRenderer>
|
||||
_initGame();
|
||||
}
|
||||
|
||||
void _loadLevel(int mapIndex) {
|
||||
// Grab the specific level from the singleton
|
||||
_currentLevel = Wolf3d.I.levels[mapIndex];
|
||||
Future<void> _initGame() async {
|
||||
// 1. Setup our starting indices
|
||||
_currentEpisodeIndex = widget.startingEpisode;
|
||||
_currentLevelIndex = 0;
|
||||
|
||||
// Play the exact track id Software intended for this level!
|
||||
Wolf3d.I.audio.playLevelMusic(_currentLevel);
|
||||
// 2. Load the first floor!
|
||||
_loadLevel();
|
||||
|
||||
// TODO: Initialize player position, spawn enemies based on difficulty, etc.
|
||||
debugPrint("Loaded Level: ${_currentLevel.name}");
|
||||
}
|
||||
_gameLoop = createTicker(_tick)..start();
|
||||
_focusNode.requestFocus();
|
||||
|
||||
void _onLevelCompleted() {
|
||||
Wolf3d.I.audio.stopMusic();
|
||||
// When the player hits the elevator switch, advance the map
|
||||
setState(() {
|
||||
_currentMapIndex++;
|
||||
|
||||
// Check if they beat the episode (each episode is 10 levels)
|
||||
int maxLevelForEpisode = (widget.startingEpisode * 10) + 9;
|
||||
|
||||
if (_currentMapIndex > maxLevelForEpisode) {
|
||||
// TODO: Handle episode completion (show victory screen, return to menu)
|
||||
debugPrint("Episode Completed!");
|
||||
} else {
|
||||
_loadLevel(_currentMapIndex);
|
||||
}
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initGame() async {
|
||||
// 1. Calculate the starting index
|
||||
_currentMapIndex = widget.startingEpisode * 10;
|
||||
void _loadLevel() {
|
||||
// 1. Clean up the previous level's state
|
||||
entities.clear();
|
||||
damageFlashOpacity = 0.0;
|
||||
|
||||
// 2. Load the initial level data
|
||||
_loadLevel(_currentMapIndex);
|
||||
// 2. Grab the exact level from our new Episode hierarchy
|
||||
final episode = widget.data.episodes[_currentEpisodeIndex];
|
||||
activeLevel = episode.levels[_currentLevelIndex];
|
||||
|
||||
// Get the first level out of the data class
|
||||
activeLevel = widget.data.levels.first;
|
||||
|
||||
// Set up your grids directly from the active level
|
||||
currentLevel = activeLevel.wallGrid;
|
||||
// 3. DEEP COPY the wall grid! If we don't do this, destroying walls/doors
|
||||
// will permanently corrupt the map data in the Wolf3d singleton.
|
||||
currentLevel = List.generate(
|
||||
64,
|
||||
(y) => List.from(activeLevel.wallGrid[y]),
|
||||
);
|
||||
final Level objectLevel = activeLevel.objectGrid;
|
||||
|
||||
// 4. Initialize Managers
|
||||
doorManager.initDoors(currentLevel);
|
||||
pushwallManager.initPushwalls(currentLevel, objectLevel);
|
||||
|
||||
// 5. Play Music
|
||||
Wolf3d.I.audio.playLevelMusic(activeLevel);
|
||||
|
||||
// 6. Spawn Player and Entities
|
||||
for (int y = 0; y < 64; y++) {
|
||||
for (int x = 0; x < 64; x++) {
|
||||
int objId = objectLevel[y][x];
|
||||
@@ -119,25 +115,17 @@ class _WolfRendererState extends State<WolfRenderer>
|
||||
|
||||
if (objId >= MapObject.playerNorth && objId <= MapObject.playerWest) {
|
||||
double spawnAngle = 0.0;
|
||||
switch (objId) {
|
||||
case MapObject.playerNorth:
|
||||
spawnAngle = 3 * math.pi / 2;
|
||||
break;
|
||||
case MapObject.playerEast:
|
||||
spawnAngle = 0.0;
|
||||
break;
|
||||
case MapObject.playerSouth:
|
||||
spawnAngle = math.pi / 2;
|
||||
break;
|
||||
case MapObject.playerWest:
|
||||
spawnAngle = math.pi;
|
||||
break;
|
||||
if (objId == MapObject.playerNorth) {
|
||||
spawnAngle = 3 * math.pi / 2;
|
||||
} else if (objId == MapObject.playerEast) {
|
||||
spawnAngle = 0.0;
|
||||
} else if (objId == MapObject.playerSouth) {
|
||||
spawnAngle = math.pi / 2;
|
||||
} else if (objId == MapObject.playerWest) {
|
||||
spawnAngle = math.pi;
|
||||
}
|
||||
player = Player(
|
||||
x: x + 0.5,
|
||||
y: y + 0.5,
|
||||
angle: spawnAngle,
|
||||
);
|
||||
|
||||
player = Player(x: x + 0.5, y: y + 0.5, angle: spawnAngle);
|
||||
} else {
|
||||
Entity? newEntity = EntityRegistry.spawn(
|
||||
objId,
|
||||
@@ -147,31 +135,54 @@ class _WolfRendererState extends State<WolfRenderer>
|
||||
widget.data.sprites.length,
|
||||
isSharewareMode: widget.data.version == GameVersion.shareware,
|
||||
);
|
||||
|
||||
if (newEntity != null) {
|
||||
entities.add(newEntity);
|
||||
}
|
||||
if (newEntity != null) entities.add(newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Clear non-solid blocks from the collision grid
|
||||
for (int y = 0; y < 64; y++) {
|
||||
for (int x = 0; x < 64; x++) {
|
||||
int id = currentLevel[y][x];
|
||||
if ((id >= 1 && id <= 63) || (id >= 90 && id <= 101)) {
|
||||
// Leave walls and doors solid
|
||||
} else {
|
||||
if (!((id >= 1 && id <= 63) || (id >= 90 && id <= 101))) {
|
||||
currentLevel[y][x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_bumpPlayerIfStuck();
|
||||
_gameLoop = createTicker(_tick)..start();
|
||||
_focusNode.requestFocus();
|
||||
debugPrint("Loaded Floor: ${_currentLevelIndex + 1} - ${activeLevel.name}");
|
||||
}
|
||||
|
||||
void _onLevelCompleted({bool isSecretExit = false}) {
|
||||
Wolf3d.I.audio.stopMusic();
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
final currentEpisode = widget.data.episodes[_currentEpisodeIndex];
|
||||
|
||||
if (isSecretExit) {
|
||||
// Save the next normal map index so we can return to it later
|
||||
_returnLevelIndex = _currentLevelIndex + 1;
|
||||
_currentLevelIndex = 9; // Jump to the secret map
|
||||
debugPrint("Found the Secret Exit!");
|
||||
} else {
|
||||
// Are we currently ON the secret map, and need to return?
|
||||
if (_currentLevelIndex == 9 && _returnLevelIndex != null) {
|
||||
_currentLevelIndex = _returnLevelIndex!;
|
||||
_returnLevelIndex = null;
|
||||
} else {
|
||||
_currentLevelIndex++; // Normal progression
|
||||
}
|
||||
}
|
||||
|
||||
// Did we just beat the last map in the episode (Map 9) or the secret map (Map 10)?
|
||||
if (_currentLevelIndex >= currentEpisode.levels.length ||
|
||||
_currentLevelIndex > 9) {
|
||||
debugPrint("Episode Completed! You win!");
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
_loadLevel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -299,11 +310,37 @@ class _WolfRendererState extends State<WolfRenderer>
|
||||
}
|
||||
|
||||
if (inputManager.isInteracting) {
|
||||
doorManager.handleInteraction(
|
||||
player.x,
|
||||
player.y,
|
||||
player.angle,
|
||||
);
|
||||
// 1. Calculate the tile exactly 1 block in front of the player
|
||||
int targetX = (player.x + math.cos(player.angle)).toInt();
|
||||
int targetY = (player.y + math.sin(player.angle)).toInt();
|
||||
|
||||
// Ensure we don't check outside the map bounds
|
||||
if (targetX >= 0 && targetX < 64 && targetY >= 0 && targetY < 64) {
|
||||
// 2. Check the WALL grid for the physical switch texture
|
||||
int wallId = currentLevel[targetY][targetX];
|
||||
if (wallId == MapObject.normalElevatorSwitch) {
|
||||
// Player hit the switch!
|
||||
_onLevelCompleted(isSecretExit: false);
|
||||
return (movement: const Coordinate2D(0, 0), dAngle: 0.0);
|
||||
} else if (wallId == MapObject.secretElevatorSwitch) {
|
||||
_onLevelCompleted(isSecretExit: true);
|
||||
return (movement: const Coordinate2D(0, 0), dAngle: 0.0);
|
||||
}
|
||||
|
||||
// 3. Check the OBJECT grid for invisible floor triggers
|
||||
// (Some custom maps use these instead of wall switches)
|
||||
int objId = activeLevel.objectGrid[targetY][targetX];
|
||||
if (objId == MapObject.normalExitTrigger) {
|
||||
_onLevelCompleted(isSecretExit: false);
|
||||
return (movement: movement, dAngle: dAngle);
|
||||
} else if (objId == MapObject.secretExitTrigger) {
|
||||
_onLevelCompleted(isSecretExit: true);
|
||||
return (movement: movement, dAngle: dAngle);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If it wasn't an elevator, try opening a door or pushing a wall
|
||||
doorManager.handleInteraction(player.x, player.y, player.angle);
|
||||
pushwallManager.handleInteraction(
|
||||
player.x,
|
||||
player.y,
|
||||
|
||||
Reference in New Issue
Block a user