Qualification-Thesis/assets/code/maze-ascension/maze_generation.rs

179 lines
5.6 KiB
Rust

/// Spawns a new maze floor in response to a SpawnMaze trigger event
pub(super) fn spawn_maze(
trigger: Trigger<SpawnMaze>,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
maze_query: Query<(Entity, &Floor, &Maze)>,
global_config: Res<GlobalMazeConfig>,
) {
let SpawnMaze { floor, config } = trigger.event();
if maze_query.iter().any(|(_, f, _)| f.0 == *floor) {
warn!("Floor {} already exists, skipping creation", floor);
return;
}
let maze = match generate_maze(config) {
Ok(m) => m,
Err(e) => {
error!("Failed to generate maze for floor {floor}: {:?}", e);
return;
}
};
// Calculate vertical offset based on floor number
let y_offset = match *floor {
1 => 0, // Ground/Initial floor (floor 1) is at y=0
_ => FLOOR_Y_OFFSET, // Other floors are offset vertically
} as f32;
let entity = commands
.spawn((
Name::new(format!("Floor {}", floor)),
HexMaze,
maze.clone(),
Floor(*floor),
config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset)),
Visibility::Visible,
))
.insert_if(CurrentFloor, || *floor == 1) // Only floor 1 gets CurrentFloor
.insert_if(NextFloor, || *floor != 1) // All other floors get NextFloor
.id();
let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config);
spawn_maze_tiles(
&mut commands,
entity,
&maze,
&assets,
config,
&global_config,
);
}
/// Spawns all tiles for a maze as children of the parent maze entity
pub fn spawn_maze_tiles(
commands: &mut Commands,
parent_entity: Entity,
maze: &Maze,
assets: &MazeAssets,
maze_config: &MazeConfig,
global_config: &GlobalMazeConfig,
) {
commands.entity(parent_entity).with_children(|parent| {
for tile in maze.values() {
spawn_single_hex_tile(
parent,
assets,
tile,
maze_config,
global_config,
);
}
});
}
/// Spawns a single hexagonal tile with appropriate transforms and materials
pub(super) fn spawn_single_hex_tile(
parent: &mut ChildBuilder,
assets: &MazeAssets,
tile: &HexTile,
maze_config: &MazeConfig,
global_config: &GlobalMazeConfig,
) {
let world_pos = tile.to_vec3(&maze_config.layout);
let rotation = match maze_config.layout.orientation {
// Pointy hexagons don't need additional rotation (0 degrees)
HexOrientation::Pointy => Quat::from_rotation_y(0.0),
// Flat-top hexagons need 30 degrees (pi/6) rotation around Y axis
HexOrientation::Flat => Quat::from_rotation_y(FRAC_PI_6),
};
// Select material based on tile position: start, end, or default
let material = match tile.pos() {
pos if pos == maze_config.start_pos => assets
.custom_materials
.get(&RosePine::Pine)
.cloned()
.unwrap_or_default(),
pos if pos == maze_config.end_pos => assets
.custom_materials
.get(&RosePine::Love)
.cloned()
.unwrap_or_default(),
_ => assets.hex_material.clone(),
};
parent
.spawn((
Name::new(format!("Hex {}", tile)),
Tile,
Mesh3d(assets.hex_mesh.clone()),
MeshMaterial3d(material),
Transform::from_translation(world_pos).with_rotation(rotation),
))
.with_children(|parent| {
spawn_walls(parent, assets, tile.walls(), global_config)
});
}
/// Spawns walls around a hexagonal tile based on the walls configuration
fn spawn_walls(
parent: &mut ChildBuilder,
assets: &MazeAssets,
walls: &Walls,
global_config: &GlobalMazeConfig,
) {
// Base rotation for wall alignment (90 degrees counter-clockwise)
let z_rotation = Quat::from_rotation_z(-FRAC_PI_2);
let y_offset = global_config.height / 2.;
for i in 0..6 {
if !walls.contains(i) {
continue;
}
// Calculate the angle for this wall
// FRAC_PI_3 = 60 deg
// Negative because going clockwise
// i * 60 produces: 0, 60, 120, 180, 240, 300
let wall_angle = -FRAC_PI_3 * i as f32;
// cos(angle) gives x coordinate on unit circle
// sin(angle) gives z coordinate on unit circle
// Multiply by wall_offset to get actual distance from center
let x_offset = global_config.wall_offset() * f32::cos(wall_angle);
let z_offset = global_config.wall_offset() * f32::sin(wall_angle);
// x: distance along x-axis from center
// y: vertical offset from center
// z: distance along z-axis from center
let pos = Vec3::new(x_offset, y_offset, z_offset);
// 1. Rotate around x-axis to align wall with angle
// 2. Add FRAC_PI_2 (90) to make wall perpendicular to angle
let x_rotation = Quat::from_rotation_x(wall_angle + FRAC_PI_2);
let final_rotation = z_rotation * x_rotation;
spawn_single_wall(parent, assets, final_rotation, pos);
}
}
/// Spawns a single wall segment with the specified rotation and position
fn spawn_single_wall(
parent: &mut ChildBuilder,
assets: &MazeAssets,
rotation: Quat,
offset: Vec3,
) {
parent.spawn((
Name::new("Wall"),
Wall,
Mesh3d(assets.wall_mesh.clone()),
MeshMaterial3d(assets.wall_material.clone()),
Transform::from_translation(offset).with_rotation(rotation),
));
}