///|
const MAX_DEPTH = 800.0
///|
const DOT_RADIUS = 4.0
///|
enum Side {
Vertical
Horizontal
}
///|
fn cast_ray(angle : Double) -> Ray {
let start_x = player_state.x
let start_y = player_state.y
// Ray direction
let ray_dir_x = @math.cos(angle)
let ray_dir_y = @math.sin(angle)
// Step size for DDA algorithm
let delta_dist_x = if ray_dir_x != 0.0 {
(1.0 / ray_dir_x).abs()
} else {
@double.infinity
}
let delta_dist_y = if ray_dir_y != 0.0 {
(1.0 / ray_dir_y).abs()
} else {
@double.infinity
}
// Current position in map coordinates
let mut map_x = (start_x / TILE_SIZE).floor()
let mut map_y = (start_y / TILE_SIZE).floor()
let step_x = if ray_dir_x < 0.0 { -1.0 } else { 1.0 }
let step_y = if ray_dir_y < 0.0 { -1.0 } else { 1.0 }
let mut side_dist_x = if ray_dir_x < 0.0 {
(start_x / TILE_SIZE - map_x) * delta_dist_x
} else {
(map_x + 1.0 - start_x / TILE_SIZE) * delta_dist_x
}
let mut side_dist_y = if ray_dir_y < 0.0 {
(start_y / TILE_SIZE - map_y) * delta_dist_y
} else {
(map_y + 1.0 - start_y / TILE_SIZE) * delta_dist_y
}
// Perform DDA
let mut hit = false
let mut side = Vertical
while !hit {
// Jump to next map square, either in x-direction or in y-direction
if side_dist_x < side_dist_y {
side_dist_x += delta_dist_x
map_x += step_x
side = Vertical
} else {
side_dist_y += delta_dist_y
map_y += step_y
side = Horizontal
}
// Check if ray has hit a wall
if is_wall(map_x * TILE_SIZE, map_y * TILE_SIZE) {
hit = true
}
// Safety check to prevent infinite loops
if (map_x - start_x / TILE_SIZE).abs() > MAX_DEPTH / TILE_SIZE ||
(map_y - start_y / TILE_SIZE).abs() > MAX_DEPTH / TILE_SIZE {
break
}
}
// Calculate distance
let perp_wall_dist = match side {
Vertical => (map_x - start_x / TILE_SIZE + (1 - step_x) / 2) / ray_dir_x
Horizontal => (map_y - start_y / TILE_SIZE + (1 - step_y) / 2) / ray_dir_y
}
// Convert back to pixel distance
let distance = perp_wall_dist * TILE_SIZE
// Calculate wall hit coordinate for texture mapping
let wall_hit_x = start_x + perp_wall_dist * ray_dir_x
let wall_hit_y = start_y + perp_wall_dist * ray_dir_y
// Use the appropriate coordinate based on wall side for texture mapping
let wall_x = match side {
Vertical => wall_hit_y / TILE_SIZE // Use Y coordinate for vertical walls
Horizontal => wall_hit_x / TILE_SIZE // Use X coordinate for horizontal walls
}
// Get fractional part for texture coordinate
let wall_x = wall_x - wall_x.floor()
let hit_dots = []
// Check each dot to see if ray passes close to it
for _i, dot in dots {
if !dot.collected {
// Calculate dot distance from player
let dot_dx = dot.x - start_x
let dot_dy = dot.y - start_y
let dot_distance = (dot_dx * dot_dx + dot_dy * dot_dy).sqrt()
// Check if dot is roughly in the same direction as the ray
let dot_angle = @math.atan2(dot_dy, dot_dx)
let view_angle = @math.atan2(DOT_RADIUS, dot_distance)
let angle_diff = (dot_angle - angle).abs()
let angle_diff = @cmp.minimum(angle_diff, @math.PI * 2 - angle_diff)
// If dot is close to ray direction and closer than wall
if angle_diff < view_angle && dot_distance < distance {
hit_dots.push((dot_distance, angle_diff))
}
}
}
let hit_ghosts = []
// Check each ghost to see if ray passes close to it
for i, ghost in ghosts {
// Calculate ghost distance from player
let ghost_dx = ghost.x - start_x
let ghost_dy = ghost.y - start_y
let ghost_distance = (ghost_dx * ghost_dx + ghost_dy * ghost_dy).sqrt()
// Check if ghost is roughly in the same direction as the ray
let ghost_angle = @math.atan2(ghost_dy, ghost_dx)
let view_angle = @math.atan2(GHOST_RADIUS, ghost_distance)
let angle_diff = (ghost_angle - angle).abs()
let angle_diff = @cmp.minimum(angle_diff, @math.PI * 2.0 - angle_diff)
// If ghost is close to ray direction and closer than wall
if angle_diff < view_angle && ghost_distance < distance {
hit_ghosts.push((i, ghost_distance, angle_diff))
}
}
{ distance, side, wall_x, hit_dots, hit_ghosts }
}
///|
struct Ray {
distance : Double
side : Side
wall_x : Double // X coordinate where ray hit the wall (for texture mapping)
hit_dots : Array[(Double, Double)] // Distance to a dot
hit_ghosts : Array[(Int, Double, Double)] // (ghost_index, distance, angle_diff)
}
///|
const FOV : Double = @math.PI / 3.0 // 60 degrees
///|
fn cast_all_rays() -> Array[Ray] {
let rays = []
let num_rays = (CANVAS_WIDTH / 2).to_int() // Assuming each ray takes 2 pixels
let angle_step = FOV / num_rays.to_double()
for i in 0..