Separate floor from fixture

This commit is contained in:
StephenAdamson
2024-12-29 00:45:07 +00:00
parent 7da420a66e
commit 3aadb8bb78
24 changed files with 269 additions and 304 deletions
+115 -214
View File
@@ -4,62 +4,44 @@ use crate::item::Item;
use bevy::ecs::system::ParamSet;
use bevy::prelude::*;
use std::collections::HashMap;
use std::process::exit;
#[derive(Component, Clone)]
#[require(Sprite)]
pub struct Tile {
pub struct FloorTile {
pub id: u32,
pub opaque: bool,
pub visible_range: [u32; 8],
}
impl Default for Tile {
fn default() -> Self {
Self {
id: 1,
opaque: true,
visible_range: [0; 8],
}
}
}
#[derive(Component)]
#[require(Tile)]
pub struct FloorTile {
pub walkable: bool,
pub astar_weight: u8,
pub visible_range: [u32; 8],
}
impl Default for FloorTile {
fn default() -> Self {
Self {
id: 0,
opaque: true,
walkable: true,
astar_weight: 1,
astar_weight: 0,
visible_range: [0; 8],
}
}
}
#[derive(Component)]
pub struct WallTile {
pub tile: Tile,
#[derive(Component, Clone)]
pub struct FixtureTile {
pub id: u32,
pub solid: bool,
pub embrasure: bool, // Arrowslit, crenelle, grate, cage etc
pub visible_range: [u32; 8],
}
#[derive(Component)]
#[require(Tile)]
pub struct HasWater {
pub height: u8,
pub astarmultiplier: f32,
pub swimmable: bool,
}
#[derive(Component)]
pub struct DoorTile {
pub tile: Tile,
pub open: bool,
pub locked: bool,
impl Default for FixtureTile {
fn default() -> Self {
Self {
id: 0,
solid: true,
visible_range: [0; 8],
}
}
}
#[derive(Resource, Default)]
@@ -76,63 +58,61 @@ pub fn camera_z_movement(
z_index.0 -= 1.0;
z_index.0 = z_index.0.clamp(-149.0, 5.0);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
if keyboard_input.just_pressed(KeyCode::ShiftRight) {
z_index.0 += 1.0;
z_index.0 = z_index.0.clamp(-149.0, 5.0);
camera_moved.0 = true;
println!("z_index = {}", z_index.0);
}
}
pub fn tile_sprite_generate_occlusion_map(
mut query_set: ParamSet<(Query<(&Transform, &Tile)>, Query<(&mut Tile, &Transform)>)>,
mut query_set: ParamSet<(
Query<(&Transform, &FloorTile)>,
Query<(&Transform, &FixtureTile)>,
Query<(&mut FloorTile, &Transform)>,
Query<(&mut FixtureTile, &Transform)>,
)>,
) {
// First, create the tile map using only the first query
// Create the tile map using floor query
let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = {
let first_query = query_set.p0();
first_query
.iter()
.map(|(transform, tile)| {
let mut map = HashMap::new();
// Add floor tiles
query_set.p0().iter().for_each(|(transform, tile)| {
map.insert(
(
(
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
),
(tile.opaque, tile.id),
)
})
.collect()
};
// Then, process tiles using the second query and our collected tile_map
let mut second_query = query_set.p1();
second_query
.par_iter_mut()
.for_each(|(mut tile, transform)| {
// Clear existing visible range
tile.visible_range = [0; 8];
let pos = (
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
),
(tile.opaque, tile.id), // false indicates floor
);
});
map
};
let tile_size = TILE_SIZE as i32;
// Helper function to calculate visibility
let calculate_visibility =
|pos: (i32, i32, i32), tile_map: &HashMap<(i32, i32, i32), (bool, u32)>| {
let mut visible_range = [0u32; 8];
// Check visibility for each possible z-index
for mut camera_z in -150..100 {
camera_z *= TILE_SIZE as i32;
camera_z *= tile_size;
let mut is_visible = false;
// Skip if tile is above camera
if pos.2 > (camera_z) {
if pos.2 > camera_z {
continue;
}
// Check for opaque tiles above
let mut is_occluded = false;
'vertical_check: for z_offset in 1..=20 {
let above_pos = (pos.0, pos.1, pos.2 + (z_offset * TILE_SIZE as i32));
'vertical_check: for z_offset in 1..=35 {
// check above
let above_pos = (pos.0, pos.1, pos.2 + (z_offset * tile_size));
if above_pos.2 <= camera_z {
if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) {
if is_opaque {
@@ -146,22 +126,20 @@ pub fn tile_sprite_generate_occlusion_map(
}
if !is_occluded {
// Check neighbors for visibility
let base_pos = (pos.0, pos.1, pos.2);
'neighbor_check: for x_offset in -1..=1 {
for y_offset in -1..=1 {
for z_offset in 0..=1 {
if x_offset == 0 && y_offset == 0 && z_offset == 0 {
continue;
}
let neighbor_pos = (
pos.0 + (x_offset * TILE_SIZE as i32),
pos.1 + (y_offset * TILE_SIZE as i32),
pos.2 + (z_offset * TILE_SIZE as i32),
base_pos.0 + x_offset * tile_size,
base_pos.1 + y_offset * tile_size,
base_pos.2 + z_offset * tile_size,
);
if let Some(&(_, id)) = tile_map.get(&neighbor_pos) {
if id == 0 {
// air tile
is_visible = true;
break 'neighbor_check;
}
@@ -172,158 +150,81 @@ pub fn tile_sprite_generate_occlusion_map(
}
if is_visible {
let z2 = ((camera_z / TILE_SIZE as i32) + 150) as usize;
tile.visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32);
let z2 = ((camera_z / tile_size) + 150) as usize;
visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32);
}
}
visible_range
};
// Update floor tiles
query_set
.p2()
.par_iter_mut()
.for_each(|(mut tile, transform)| {
let pos = (
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
);
tile.visible_range = calculate_visibility(pos, &tile_map);
});
// Update fixture tiles
query_set
.p3()
.par_iter_mut()
.for_each(|(mut fixture, transform)| {
let pos = (
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32 - 1,
);
fixture.visible_range = calculate_visibility(pos, &tile_map);
});
}
pub fn update_tile_visibility(
z_index: ResMut<game::ZIndex>,
mut query: Query<(&Tile, &mut Visibility)>,
) {
let z_index = (z_index.0 as i32 + 150) as usize;
query.par_iter_mut().for_each(|(tile, mut visibility)| {
let is_visible = (tile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0;
*visibility = if is_visible {
Visibility::Visible
} else {
Visibility::Hidden
};
});
}
// TODO CHECK THIS
pub fn update_occlusion_map_for_tile(
updated_pos: Vec3,
mut query_set: ParamSet<(
Query<(&Transform, &Tile)>,
Query<(&mut Visibility, &mut Tile, &Transform)>,
Query<(&FloorTile, &mut Visibility)>,
Query<(&FixtureTile, &mut Visibility)>,
)>,
) {
use std::collections::HashMap;
let z_index = (z_index.0 as i32 + 150) as usize;
// Convert the updated position into integer coordinates
let updated_pos = (
updated_pos.x as i32,
updated_pos.y as i32,
updated_pos.z as i32,
);
// Update floor visibility
query_set
.p0()
.par_iter_mut()
.for_each(|(tile, mut visibility)| {
let is_visible = (tile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0;
*visibility = if is_visible {
Visibility::Visible
} else {
Visibility::Hidden
};
});
// Build a tile map as before, but only for relevant tiles
let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = {
let first_query = query_set.p0();
first_query
.iter()
.map(|(transform, tile)| {
(
(
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
),
(tile.opaque, tile.id),
)
})
.collect()
};
// Get the second query for mutable access to tiles
let mut second_query = query_set.p1();
// Iterate over a 3×3 area centered around the updated tile
for x_offset in -1..=1 {
for y_offset in -1..=1 {
for z_offset in -1..=1 {
let current_pos = (
updated_pos.0 + x_offset,
updated_pos.1 + y_offset,
updated_pos.2 + z_offset,
);
// Find the tile at the current position
if let Some((mut visibility, mut tile, _)) =
second_query.iter_mut().find(|(_, _, transform)| {
let pos = (
transform.translation.x as i32,
transform.translation.y as i32,
transform.translation.z as i32,
);
pos == current_pos
})
{
// Clear the visible range for the current tile
tile.visible_range = [0; 8];
// Recalculate visibility for each possible z-index
for camera_z in current_pos.2 - 20..=current_pos.1 + 1 {
let mut is_visible = false;
// Skip if the tile is above the camera
if current_pos.2 > camera_z {
continue;
}
// Check for occluding tiles above
let mut is_occluded = false;
for z_offset in 1..=20 {
let above_pos =
(current_pos.0, current_pos.1, current_pos.2 + z_offset);
if above_pos.2 <= camera_z {
if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) {
if is_opaque {
is_occluded = true;
break;
}
}
}
}
if !is_occluded {
// Check neighbors for visibility
'neighbor_check: for x_offset in -1..=1 {
for y_offset in -1..=1 {
for z_offset in 0..=1 {
if x_offset == 0 && y_offset == 0 && z_offset == 0 {
continue;
}
let neighbor_pos = (
current_pos.0 + x_offset,
current_pos.1 + y_offset,
current_pos.2 + z_offset,
);
if let Some(&(_, id)) = tile_map.get(&neighbor_pos) {
if id == 0 {
// sky tile
is_visible = true;
break 'neighbor_check;
}
}
}
}
}
}
if is_visible {
tile.visible_range[((camera_z + 150) / 32) as usize] |=
1 << (((camera_z + 150) % 32) as u32);
}
}
// Update visibility state
*visibility = Visibility::Hidden;
}
}
}
}
// Update fixture visibility
query_set
.p1()
.par_iter_mut()
.for_each(|(fixture, mut visibility)| {
let is_visible =
(fixture.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0;
*visibility = if is_visible {
Visibility::Visible
} else {
Visibility::Hidden
};
});
}
pub fn tile_item_sprite_update(
time: Res<Time>,
mut query_tile: Query<(Entity, &Children, &mut TileState), With<Tile>>,
mut query_tile: Query<(Entity, &Children, &mut TileState)>,
mut query_item: Query<(&mut Visibility, &Item)>,
) {
for (_, children, mut state) in query_tile.iter_mut() {