diff --git a/lib/features/renderer/renderer.dart b/lib/features/renderer/renderer.dart index fdccf81..50df099 100644 --- a/lib/features/renderer/renderer.dart +++ b/lib/features/renderer/renderer.dart @@ -51,13 +51,8 @@ class _WolfRendererState extends State List entities = []; - // Track door animations - // Key is "X,Y" coordinate. Value is how far open it is (0.0 to 1.0) Map doorOffsets = {}; - Map doorStates = {}; // 1 = opening, 2 = fully open - - double moveStepX = 0; - double moveStepY = 0; + Map doorStates = {}; @override void initState() { @@ -66,20 +61,15 @@ class _WolfRendererState extends State } Future _initGame({bool demo = true}) async { - // 1. Load the entire WL1 (demo)/WL6 (retail) data gameMap = demo ? await WolfMap.loadDemo() : await WolfMap.load(); - - // 2. Extract Level 1 (E1M1) currentLevel = gameMap.levels[0].wallGrid; final Matrix objectLevel = gameMap.levels[0].objectGrid; - // 1. SCAN FOR PLAYER SPAWN & ENTITIES for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { int objId = objectLevel[y][x]; - // Player Spawn if (objId >= 19 && objId <= 22) { double spawnAngle = 0.0; switch (objId) { @@ -117,7 +107,6 @@ class _WolfRendererState extends State } } - // 2. CLEAN UP WALLS / PRESERVE DOORS for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { int id = currentLevel[y][x]; @@ -129,7 +118,6 @@ class _WolfRendererState extends State } } - // 4. Start the game! _bumpPlayerIfStuck(); _gameLoop = createTicker(_tick)..start(); _focusNode.requestFocus(); @@ -180,10 +168,9 @@ class _WolfRendererState extends State } bool _isWalkable(int x, int y) { - if (currentLevel[y][x] == 0) return true; // Empty space + if (currentLevel[y][x] == 0) return true; if (currentLevel[y][x] >= 90) { String key = '$x,$y'; - // Allow the player to walk through if the door is > 70% open if (doorOffsets[key] != null && doorOffsets[key]! > 0.7) { return true; } @@ -191,16 +178,19 @@ class _WolfRendererState extends State return false; } + // --- ORCHESTRATOR --- void _tick(Duration elapsed) { - // 1. Inputs & Intentions - _processInputs(elapsed); + // 1. Process intentions and receive movement vectors + final movement = _processInputs(elapsed); - // 2. State Updates + // 2. Explicit State Updates player.updateWeaponSwitch(); _updateDoors(); - _applyMovementAndCollision(); + _applyMovementAndCollision(movement.dx, movement.dy); _updateEntities(elapsed); - _updateScreenEffects(); + + // Explicit reassignment from a pure(r) function + damageFlashOpacity = _calculateScreenEffects(damageFlashOpacity); // 3. Combat if (player.updateWeapon(elapsed.inMilliseconds)) { @@ -211,7 +201,6 @@ class _WolfRendererState extends State setState(() {}); } - // A helper method to handle getting shot void _takeDamage(int damage) { player.takeDamage(damage); damageFlashOpacity = 0.5; @@ -219,16 +208,14 @@ class _WolfRendererState extends State void _performRaycastAttack(Duration elapsed) { Enemy? closestEnemy; - double minDistance = 15.0; // Maximum range of the gun + double minDistance = 15.0; for (Entity entity in entities) { if (entity is Enemy && entity.state != EntityState.dead) { - // 1. Calculate the angle from player to enemy double dx = entity.x - player.x; double dy = entity.y - player.y; double angleToEnemy = math.atan2(dy, dx); - // 2. Check if that angle is close to our player's aiming angle double angleDiff = player.angle - angleToEnemy; while (angleDiff <= -math.pi) { angleDiff += 2 * math.pi; @@ -237,13 +224,10 @@ class _WolfRendererState extends State angleDiff -= 2 * math.pi; } - // 3. Simple bounding box check (approx 0.4 units wide) double dist = math.sqrt(dx * dx + dy * dy); - double threshold = - 0.2 / dist; // Smaller threshold as they get further away + double threshold = 0.2 / dist; if (angleDiff.abs() < threshold) { - // 4. Ensure there is no wall between you and the enemy if (_hasLineOfSightToEnemy(entity, dist)) { if (dist < minDistance) { minDistance = dist; @@ -260,7 +244,6 @@ class _WolfRendererState extends State elapsed.inMilliseconds, ); - // If the shot was fatal, reward the player if (closestEnemy.state == EntityState.dead) { player.score += 100; } @@ -279,45 +262,38 @@ class _WolfRendererState extends State return true; } - void _processInputs(Duration elapsed) { + // Returns movement deltas instead of modifying class variables + ({double dx, double dy}) _processInputs(Duration elapsed) { inputManager.update(); const double moveSpeed = 0.16; const double turnSpeed = 0.12; + double dx = 0; + double dy = 0; - // Reset steps each tick - moveStepX = 0; - moveStepY = 0; - - // 1. Weapon Switching if (inputManager.requestedWeapon != null) { player.requestWeaponSwitch(inputManager.requestedWeapon!); } - // 2. Firing if (inputManager.isFiring) { player.fire(elapsed.inMilliseconds); } - // 3. Movement if (inputManager.isMovingForward) { - moveStepX += math.cos(player.angle) * moveSpeed; - moveStepY += math.sin(player.angle) * moveSpeed; + dx += math.cos(player.angle) * moveSpeed; + dy += math.sin(player.angle) * moveSpeed; } if (inputManager.isMovingBackward) { - moveStepX -= math.cos(player.angle) * moveSpeed; - moveStepY -= math.sin(player.angle) * moveSpeed; + dx -= math.cos(player.angle) * moveSpeed; + dy -= math.sin(player.angle) * moveSpeed; } - // 4. Turning if (inputManager.isTurningLeft) player.angle -= turnSpeed; if (inputManager.isTurningRight) player.angle += turnSpeed; - // Keep angle wrapped cleanly if (player.angle < 0) player.angle += 2 * math.pi; if (player.angle > 2 * math.pi) player.angle -= 2 * math.pi; - // 5. Interaction (Doors) if (inputManager.isInteracting) { int targetX = (player.x + math.cos(player.angle)).toInt(); int targetY = (player.y + math.sin(player.angle)).toInt(); @@ -328,46 +304,41 @@ class _WolfRendererState extends State targetX < currentLevel[0].length) { if (currentLevel[targetY][targetX] >= 90) { String key = '$targetX,$targetY'; - // Start the animation if it isn't already opening! if (!doorStates.containsKey(key) || doorStates[key] == 0) { doorStates[key] = 1; } } } } + + return (dx: dx, dy: dy); } void _updateDoors() { doorStates.forEach((key, state) { if (state == 1) { - // 1 = opening - doorOffsets[key] = (doorOffsets[key] ?? 0.0) + 0.02; // Slide speed + doorOffsets[key] = (doorOffsets[key] ?? 0.0) + 0.02; if (doorOffsets[key]! >= 1.0) { doorOffsets[key] = 1.0; - doorStates[key] = 2; // Mark as fully open + doorStates[key] = 2; } } }); } - void _applyMovementAndCollision() { + // Now receives dx and dy explicitly + void _applyMovementAndCollision(double dx, double dy) { const double margin = 0.3; - // X-axis collision - double newX = player.x + moveStepX; - int checkX = (moveStepX > 0) - ? (newX + margin).toInt() - : (newX - margin).toInt(); + double newX = player.x + dx; + int checkX = (dx > 0) ? (newX + margin).toInt() : (newX - margin).toInt(); if (_isWalkable(checkX, player.y.toInt())) { player.x = newX; } - // Y-axis collision - double newY = player.y + moveStepY; - int checkY = (moveStepY > 0) - ? (newY + margin).toInt() - : (newY - margin).toInt(); + double newY = player.y + dy; + int checkY = (dy > 0) ? (newY + margin).toInt() : (newY - margin).toInt(); if (_isWalkable(player.x.toInt(), checkY)) { player.y = newY; @@ -385,14 +356,11 @@ class _WolfRendererState extends State isWalkable: _isWalkable, onDamagePlayer: _takeDamage, ); - } - // Collectible Interaction Logic - else if (entity is Collectible) { + } else if (entity is Collectible) { double dx = player.x - entity.x; double dy = player.y - entity.y; double dist = math.sqrt(dx * dx + dy * dy); - // If player is close enough to the item if (dist < 0.5) { if (player.tryPickup(entity)) { itemsToRemove.add(entity); @@ -401,17 +369,17 @@ class _WolfRendererState extends State } } - // Remove the items that were successfully picked up if (itemsToRemove.isNotEmpty) { entities.removeWhere((e) => itemsToRemove.contains(e)); } } - void _updateScreenEffects() { - // Fade out the damage flash smoothly - if (damageFlashOpacity > 0) { - damageFlashOpacity = math.max(0.0, damageFlashOpacity - 0.05); + // Takes an input and returns a value instead of implicitly changing state + double _calculateScreenEffects(double currentOpacity) { + if (currentOpacity > 0) { + return math.max(0.0, currentOpacity - 0.05); } + return currentOpacity; } @override @@ -432,7 +400,6 @@ class _WolfRendererState extends State onKeyEvent: (_) {}, child: Column( children: [ - // Game view Expanded( child: LayoutBuilder( builder: (context, constraints) { @@ -456,7 +423,6 @@ class _WolfRendererState extends State sprites: gameMap.sprites, ), ), - // Weapon Viewmodel Positioned( bottom: -20, left: 0, @@ -464,8 +430,9 @@ class _WolfRendererState extends State child: Center( child: Transform.translate( offset: Offset( - // Bobbing math - (moveStepX.abs() + moveStepY.abs()) > 0 + // Replaced hidden step variables with a direct intention check! + (inputManager.isMovingForward || + inputManager.isMovingBackward) ? math.sin( DateTime.now() .millisecondsSinceEpoch / @@ -473,7 +440,6 @@ class _WolfRendererState extends State ) * 12 : 0, - // Y-Offset for lowering and raising player.weaponAnimOffset, ), child: SizedBox( @@ -506,8 +472,6 @@ class _WolfRendererState extends State }, ), ), - - // HUD Hud(player: player), ], ),