De-coupled remaining aspects of game into packages

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-15 16:17:27 +01:00
parent 026e6d8cb4
commit 57d394e911
41 changed files with 522 additions and 44 deletions

View File

@@ -0,0 +1,7 @@
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
abstract class EngineAudio {
void playLevelMusic(WolfLevel level);
void stopMusic();
// You can easily add things like void playSoundEffect(SoundId id); later!
}

View File

@@ -0,0 +1,11 @@
import 'package:wolf_3d_entities/wolf_3d_entities.dart';
class EngineInput {
bool isMovingForward = false;
bool isMovingBackward = false;
bool isTurningLeft = false;
bool isTurningRight = false;
bool isInteracting = false;
bool isFiring = false;
WeaponType? requestedWeapon;
}

View File

@@ -1,62 +0,0 @@
import 'package:flutter/services.dart';
import 'package:wolf_3d_entities/wolf_3d_entities.dart';
class InputManager {
Set<LogicalKeyboardKey> _previousKeys = {};
bool isMovingForward = false;
bool isMovingBackward = false;
bool isTurningLeft = false;
bool isTurningRight = false;
// Discrete (triggers once per press)
bool isInteracting = false;
// Continuous
bool isFiring = false;
WeaponType? requestedWeapon;
void update() {
final pressedKeys = HardwareKeyboard.instance.logicalKeysPressed;
// Calculate all keys that were pressed exactly on this frame
final newlyPressedKeys = pressedKeys.difference(_previousKeys);
// * Movement
isMovingForward = pressedKeys.contains(LogicalKeyboardKey.keyW);
isMovingBackward = pressedKeys.contains(LogicalKeyboardKey.keyS);
isTurningLeft = pressedKeys.contains(LogicalKeyboardKey.keyA);
isTurningRight = pressedKeys.contains(LogicalKeyboardKey.keyD);
// * Interaction (Space)
// Much simpler now using the newlyPressedKeys set
isInteracting = newlyPressedKeys.contains(LogicalKeyboardKey.space);
// * Firing (Left Control)
// - Keeping this continuous for machine guns
isFiring =
pressedKeys.contains(LogicalKeyboardKey.controlLeft) &&
!pressedKeys.contains(LogicalKeyboardKey.space);
// * Manual Weapon Switching
requestedWeapon = null;
// Iterate through newly pressed keys and switch on them
for (final LogicalKeyboardKey key in newlyPressedKeys) {
switch (key) {
case LogicalKeyboardKey.digit1:
requestedWeapon = WeaponType.knife;
case LogicalKeyboardKey.digit2:
requestedWeapon = WeaponType.pistol;
case LogicalKeyboardKey.digit3:
requestedWeapon = WeaponType.machineGun;
case LogicalKeyboardKey.digit4:
requestedWeapon = WeaponType.chainGun;
}
}
// * Save state for next tick
_previousKeys = Set.from(pressedKeys);
}
}

View File

@@ -1,4 +1,3 @@
// No flutter imports allowed!
import 'dart:math' as math;
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
@@ -11,17 +10,19 @@ class WolfEngine {
required this.difficulty,
required this.startingEpisode,
required this.onGameWon,
required this.audio,
});
final WolfensteinData data;
final Difficulty difficulty;
final int startingEpisode;
final EngineAudio audio;
// Standard Dart function instead of Flutter's VoidCallback
final void Function() onGameWon;
// Managers
final InputManager inputManager = InputManager();
final DoorManager doorManager = DoorManager();
final PushwallManager pushwallManager = PushwallManager();
@@ -46,10 +47,10 @@ class WolfEngine {
}
// Expect standard Dart Duration. The host app is responsible for the loop.
void tick(Duration elapsed) {
void tick(Duration elapsed, EngineInput input) {
if (!isInitialized) return;
final inputResult = _processInputs(elapsed);
final inputResult = _processInputs(elapsed, input);
doorManager.update(elapsed);
pushwallManager.update(elapsed, currentLevel);
@@ -94,8 +95,7 @@ class WolfEngine {
doorManager.initDoors(currentLevel);
pushwallManager.initPushwalls(currentLevel, objectLevel);
// TODO: Consider abstracting audio so the engine doesn't depend on Wolf3d singleton
Wolf3d.I.audio.playLevelMusic(activeLevel);
audio.playLevelMusic(activeLevel);
for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) {
@@ -144,7 +144,7 @@ class WolfEngine {
}
void _onLevelCompleted({bool isSecretExit = false}) {
Wolf3d.I.audio.stopMusic();
audio.stopMusic();
final currentEpisode = data.episodes[_currentEpisodeIndex];
@@ -169,37 +169,39 @@ class WolfEngine {
}
}
({Coordinate2D movement, double dAngle}) _processInputs(Duration elapsed) {
inputManager.update();
({Coordinate2D movement, double dAngle}) _processInputs(
Duration elapsed,
EngineInput input,
) {
const double moveSpeed = 0.14;
const double turnSpeed = 0.10;
Coordinate2D movement = const Coordinate2D(0, 0);
double dAngle = 0.0;
if (inputManager.requestedWeapon != null) {
player.requestWeaponSwitch(inputManager.requestedWeapon!);
// Read directly from the passed-in EngineInput object
if (input.requestedWeapon != null) {
player.requestWeaponSwitch(input.requestedWeapon!);
}
if (inputManager.isFiring) {
if (input.isFiring) {
player.fire(elapsed.inMilliseconds);
} else {
player.releaseTrigger();
}
if (inputManager.isTurningLeft) dAngle -= turnSpeed;
if (inputManager.isTurningRight) dAngle += turnSpeed;
if (input.isTurningLeft) dAngle -= turnSpeed;
if (input.isTurningRight) dAngle += turnSpeed;
Coordinate2D forwardVec = Coordinate2D(
math.cos(player.angle),
math.sin(player.angle),
);
if (inputManager.isMovingForward) movement += forwardVec * moveSpeed;
if (inputManager.isMovingBackward) movement -= forwardVec * moveSpeed;
if (input.isMovingForward) movement += forwardVec * moveSpeed;
if (input.isMovingBackward) movement -= forwardVec * moveSpeed;
if (inputManager.isInteracting) {
if (input.isInteracting) {
int targetX = (player.x + math.cos(player.angle)).toInt();
int targetY = (player.y + math.sin(player.angle)).toInt();

View File

@@ -3,6 +3,8 @@
/// More dartdocs go here.
library;
export 'src/engine_audio.dart';
export 'src/engine_input.dart';
export 'src/managers/door_manager.dart';
export 'src/managers/pushwall_manager.dart';
export 'src/player/player.dart';