diff --git a/lib/features/renderer/raycast_painter.dart b/lib/features/renderer/raycast_painter.dart index 0c63531..34f84db 100644 --- a/lib/features/renderer/raycast_painter.dart +++ b/lib/features/renderer/raycast_painter.dart @@ -11,6 +11,7 @@ class RaycasterPainter extends CustomPainter { final LinearCoordinates player; final double playerAngle; final double fov; + final Map doorOffsets; RaycasterPainter({ required this.map, @@ -18,6 +19,7 @@ class RaycasterPainter extends CustomPainter { required this.player, required this.playerAngle, required this.fov, + required this.doorOffsets, }); @override @@ -61,6 +63,11 @@ class RaycasterPainter extends CustomPainter { bool hitOutOfBounds = false; int side = 0; int hitWallId = 0; + double doorOffset = + 0.0; // Track the slide amount to pass to the texture drawer + + Set ignoredDoors = + {}; // Prevent the ray from checking the same door gap twice if (rayDirX < 0) { stepX = -1; @@ -94,8 +101,33 @@ class RaycasterPainter extends CustomPainter { mapX < 0 || mapX >= map[0].length) { hit = true; - hitOutOfBounds = true; // Safely abort if we clip out of bounds + hitOutOfBounds = true; } else if (map[mapY][mapX] > 0) { + // NEW: DOOR PASS-THROUGH LOGIC + String doorKey = '$mapX,$mapY'; + if (map[mapY][mapX] >= 90 && !ignoredDoors.contains(doorKey)) { + double currentOffset = doorOffsets[doorKey] ?? 0.0; + + if (currentOffset > 0.0) { + // Calculate exactly where the ray hit the block + double perpWallDistTemp = (side == 0) + ? (sideDistX - deltaDistX) + : (sideDistY - deltaDistY); + double wallXTemp = (side == 0) + ? player.y + perpWallDistTemp * rayDirY + : player.x + perpWallDistTemp * rayDirX; + wallXTemp -= wallXTemp.floor(); + + // If the hit coordinate is less than the open amount, the ray passes through! + if (wallXTemp < currentOffset) { + ignoredDoors.add(doorKey); + continue; // Skip the rest of this loop iteration and keep raycasting! + } + } + doorOffset = + currentOffset; // Save the offset so we can slide the texture + } + hit = true; hitWallId = map[mapY][mapX]; } @@ -127,6 +159,7 @@ class RaycasterPainter extends CustomPainter { size, hitWallId, textures, + doorOffset, ); } } @@ -140,28 +173,29 @@ class RaycasterPainter extends CustomPainter { Size size, int hitWallId, List> textures, + double doorOffset, ) { if (distance <= 0.01) distance = 0.01; double wallHeight = size.height / distance; int drawStart = ((size.height / 2) - (wallHeight / 2)).toInt(); - // 1. PERFECT TEXTURE MAPPING + // TEXTURE MAPPING // Wolf3D stores textures in pairs. Even = N/S (Light), Odd = E/W (Dark). int texNum = ((hitWallId - 1) * 2).clamp(0, textures.length - 2); + int texX = (wallX * 64).toInt().clamp(0, 63); // INTERCEPT DOORS if (hitWallId >= 90) { // Safely clamp the door texture index so it never crashes the paint loop! texNum = 98.clamp(0, textures.length - 1); + texX = ((wallX - doorOffset) * 64).toInt().clamp(0, 63); } else { // Standard wall texture pairing texNum = ((hitWallId - 1) * 2).clamp(0, textures.length - 2); if (side == 1) texNum += 1; } - int texX = (wallX * 64).toInt().clamp(0, 63); - if (side == 0 && math.cos(playerAngle) > 0) texX = 63 - texX; if (side == 1 && math.sin(playerAngle) < 0) texX = 63 - texX; diff --git a/lib/features/renderer/renderer.dart b/lib/features/renderer/renderer.dart index d2079b3..ea35738 100644 --- a/lib/features/renderer/renderer.dart +++ b/lib/features/renderer/renderer.dart @@ -30,6 +30,11 @@ class _WolfRendererState extends State bool _isLoading = true; bool _spaceWasPressed = false; + // 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 + @override void initState() { super.initState(); @@ -131,16 +136,38 @@ class _WolfRendererState extends State } } + bool _isWalkable(int x, int y) { + if (currentLevel[y][x] == 0) return true; // Empty space + 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; + } + } + return false; + } + void _tick(Duration elapsed) { const double moveSpeed = 0.12; const double turnSpeed = 0.08; + // 1. ANIMATE DOORS + doorStates.forEach((key, state) { + if (state == 1) { + // If opening + doorOffsets[key] = (doorOffsets[key] ?? 0.0) + 0.02; // Slide speed + if (doorOffsets[key]! >= 1.0) { + doorOffsets[key] = 1.0; + doorStates[key] = 2; // Mark as fully open + } + } + }); + double moveStepX = 0; double moveStepY = 0; - final pressedKeys = HardwareKeyboard.instance.logicalKeysPressed; - // 1. Calculate intended movement amounts if (pressedKeys.contains(LogicalKeyboardKey.keyW)) { moveStepX += math.cos(playerAngle) * moveSpeed; moveStepY += math.sin(playerAngle) * moveSpeed; @@ -149,8 +176,6 @@ class _WolfRendererState extends State moveStepX -= math.cos(playerAngle) * moveSpeed; moveStepY -= math.sin(playerAngle) * moveSpeed; } - - // 2. Handle Turning if (pressedKeys.contains(LogicalKeyboardKey.keyA)) { playerAngle -= turnSpeed; } @@ -160,18 +185,14 @@ class _WolfRendererState extends State if (playerAngle < 0) playerAngle += 2 * math.pi; if (playerAngle > 2 * math.pi) playerAngle -= 2 * math.pi; - // 3. Wall Sliding Collision (with Hitbox Margin!) - // A 0.3 margin keeps the camera plane safely out of the walls + // 2. UPDATED WALL COLLISION const double margin = 0.3; - double newX = player.x + moveStepX; - // Check the edge of our hitbox, not just the center point int checkX = (moveStepX > 0) ? (newX + margin).toInt() : (newX - margin).toInt(); - // Try to move along the X axis - if (currentLevel[player.y.toInt()][checkX] == 0) { + if (_isWalkable(checkX, player.y.toInt())) { player = (x: newX, y: player.y); } @@ -180,12 +201,11 @@ class _WolfRendererState extends State ? (newY + margin).toInt() : (newY - margin).toInt(); - // Try to move along the Y axis - if (currentLevel[checkY][player.x.toInt()] == 0) { + if (_isWalkable(player.x.toInt(), checkY)) { player = (x: player.x, y: newY); } - // 4. DOOR INTERACTION LOGIC + // 3. UPDATED DOOR INTERACTION bool isSpacePressed = pressedKeys.contains(LogicalKeyboardKey.space); if (isSpacePressed && !_spaceWasPressed) { @@ -196,10 +216,12 @@ class _WolfRendererState extends State targetY < currentLevel.length && targetX > 0 && targetX < currentLevel[0].length) { - int targetBlock = currentLevel[targetY][targetX]; - - if (targetBlock >= 90) { - currentLevel[targetY][targetX] = 0; + 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; + } } } } @@ -230,6 +252,7 @@ class _WolfRendererState extends State player: player, playerAngle: playerAngle, fov: fov, + doorOffsets: doorOffsets, ), ); },