refactor: Non-breaking refactor

- Everything should work as before
- Added blank submodules in layout
- Added blank submodules in player
- Moved inventory::ui submodule to ui::inventory
- Added blank submodules in ui::inventory
- Re-exported ui::inventory as inventory::ui for temporary compatibility
- Split tests submodules into different files
This commit is contained in:
Alexey 2026-03-19 12:36:51 +03:00
commit 3094a8af13
14 changed files with 880 additions and 872 deletions

0
src/layout/container.rs Normal file
View file

0
src/layout/door.rs Normal file
View file

221
src/layout/mod.rs Normal file
View file

@ -0,0 +1,221 @@
use bevy::{ecs::query::QueryFilter, prelude::*};
use bevy_rapier2d::prelude::*;
use crate::{GameState, PIXELS_PER_METER, inventory::{ActiveInventory, Inventory, item::Item}, player::Player};
pub mod container;
pub mod door;
pub mod systems;
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
#[derive(Component)]
pub struct MayInteract;
#[derive(Component, Default)]
pub struct InteractiveObject;
#[derive(Component)]
pub struct Wall;
#[derive(Component)]
#[require(Sprite, InteractiveObject)]
pub struct Door(pub i8);
#[derive(Component)]
#[require(Sprite, InteractiveObject, Inventory)]
pub struct Crate;
#[derive(Component)]
pub struct Locked;
#[derive(EntityEvent)]
pub struct InteractionEvent {
pub entity: Entity,
}
fn get_interactive_id<F: QueryFilter>(
tested_id: Entity,
interactive_query: Query<(), F>,
parent_query: Query<&ChildOf, With<Collider>>,
) -> Option<Entity> {
if interactive_query.get(tested_id).is_ok() {
return Some(tested_id);
}
let Ok(parent_id) = parent_query.get(tested_id) else {
return None;
};
match interactive_query.get(parent_id.0) {
Ok(_) => Some(parent_id.0),
Err(_) => None,
}
}
pub fn detect_interact_collisions(
mut commands: Commands,
mut collision_events: MessageReader<CollisionEvent>,
player_query: Query<(), With<Player>>,
interactive_query1: Query<(), (With<InteractiveObject>, Without<MayInteract>)>,
interactive_query2: Query<(), (With<InteractiveObject>, With<MayInteract>)>,
parent_query: Query<&ChildOf, With<Collider>>,
) {
for collision_event in collision_events.read() {
match collision_event {
CollisionEvent::Started(first, second, _) => {
let Some(interactive_id) = get_interactive_id(*first, interactive_query1, parent_query) else {
continue;
};
if player_query.get(*second).is_err() {
continue;
}
commands.entity(interactive_id).insert(MayInteract);
},
CollisionEvent::Stopped(first, second, _) => {
let Some(interactive_id) = get_interactive_id(*first, interactive_query2, parent_query) else {
continue;
};
if player_query.get(*second).is_err() {
continue;
}
commands.entity(interactive_id).remove::<MayInteract>();
},
}
}
}
fn on_crate_interact(
event: On<InteractionEvent>,
mut commands: Commands,
locked_query: Query<(), With<Locked>>,
crate_query: Query<(), With<Crate>>,
mut next_state: ResMut<NextState<GameState>>,
) {
if locked_query.get(event.entity).is_ok() {
return;
}
if crate_query.get(event.entity).is_err() {
return;
}
commands.entity(event.entity).insert(ActiveInventory);
next_state.set(GameState::Inventory);
}
fn crate_bundle(image: Handle<Image>, position: Vec2, inventory_size: UVec2, items: Vec<Item>) -> impl Bundle {
(
Crate,
Transform::from_xyz(position.x, position.y - PIXELS_PER_METER * 0.5, 0.),
Sprite::from_image(image),
Inventory::new(inventory_size),
Observer::new(on_crate_interact),
Children::spawn((
SpawnIter(items.into_iter()),
Spawn((
Collider::cuboid(PIXELS_PER_METER, PIXELS_PER_METER),
Sensor,
Transform::from_xyz(0., PIXELS_PER_METER * 0.5, 0.),
)),
)),
)
}
fn on_door_interact(
event: On<InteractionEvent>,
mut commands: Commands,
locked_query: Query<(), With<Locked>>,
collider_query: Query<(), (With<Door>, With<Collider>)>,
no_collider_query: Query<(), (With<Door>, Without<Collider>)>,
door_query: Query<(&Door, &Children)>,
mut sprite_query: Query<(&mut Sprite, &mut Transform)>,
asset_server: Res<AssetServer>,
) {
if locked_query.get(event.entity).is_ok() {
return;
}
let was_opened = if collider_query.get(event.entity).is_ok() { false }
else if no_collider_query.get(event.entity).is_ok() { true }
else {
return;
};
let Ok((door, children)) = door_query.get(event.entity) else {
return;
};
for child in children {
if let Ok((mut sprite, mut transform)) = sprite_query.get_mut(*child) {
let (image, translation) = if was_opened { (DOOR_CLOSED_ASSET, 0.) }
else { (DOOR_OPENED_ASSET, door.0 as f32 * PIXELS_PER_METER * 0.5) };
sprite.image = asset_server.load(image);
transform.translation.x = translation;
break;
}
}
if was_opened {
commands.entity(event.entity).insert(door_collider());
} else {
commands.entity(event.entity).remove::<Collider>();
}
}
fn door_collider() -> Collider {
Collider::cuboid(PIXELS_PER_METER * 0.06125, PIXELS_PER_METER)
}
fn door_bundle(image: Handle<Image>, position: Vec2, facing_left: bool) -> impl Bundle {
let direction = if facing_left { -1 } else { 1 };
(
Door(direction),
Transform::from_xyz(position.x, position.y, 0.),
Name::new(format!("Door ({}, {})", position.x, position.y)),
door_collider(),
Observer::new(on_door_interact),
children![
(
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
Sensor,
Transform::from_xyz(0., 0., 0.),
),
(
Sprite {
image,
flip_x: facing_left,
..default()
},
Transform::from_xyz(0., 0., 0.),
),
],
)
}
fn wall_bundle(position: Vec2) -> impl Bundle {
(
Wall,
Transform::from_xyz(position.x, position.y, 0.),
Collider::cuboid(PIXELS_PER_METER * 0.5, PIXELS_PER_METER),
Name::new(format!("Wall ({}, {})", position.x, position.y)),
)
}
pub fn setup_world(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let items = vec![
Item::new_positioned(uvec2(1, 1), uvec2(0, 0)),
Item::new_positioned(uvec2(2, 1), uvec2(1, 0)),
Item::new_positioned(uvec2(3, 1), uvec2(3, 0)),
Item::new_positioned(uvec2(2, 2), uvec2(6, 0)),
Item::new_positioned(uvec2(6, 2), uvec2(0, 1)),
Item::new_positioned(uvec2(2, 3), uvec2(6, 2)),
Item::new_positioned(uvec2(4, 4), uvec2(0, 4)),
];
let door_image = asset_server.load(DOOR_CLOSED_ASSET);
let crate_image = asset_server.load(CRATE_CLOSED_ASSET);
commands.spawn(door_bundle(door_image.clone(), vec2(16., 0.), true));
commands.spawn(door_bundle(door_image.clone(), vec2(48., 0.), false));
commands.spawn(door_bundle(door_image.clone(), vec2(80., 0.), false)).insert(Locked);
commands.spawn(wall_bundle(vec2(-48., 0.)));
commands.spawn(crate_bundle(crate_image.clone(), vec2(-32., 0.), uvec2(8, 8), items));
}

0
src/layout/systems.rs Normal file
View file