Compare commits

..

2 commits

Author SHA1 Message Date
5157450ced feat: Functionally working stairs without sprite
- Made all observers public
- Observe all InteractionEvents in the app, not in the entity
- Added names to layout bundles
- Raised player speed to 4 m/s for quicker debugging
- Added second container on 2nd floor
2026-03-24 16:10:43 +03:00
764e196d39 chore: Removed warnings about non-item children 2026-03-24 13:04:32 +03:00
8 changed files with 129 additions and 11 deletions

View file

@ -32,7 +32,6 @@ impl Inventory {
for entity in contained_items { for entity in contained_items {
let Ok(item) = item_query.get(*entity) else { let Ok(item) = item_query.get(*entity) else {
warn!("Could not query inventory child ({entity}), probably not item?");
continue; continue;
}; };
@ -51,7 +50,6 @@ impl Inventory {
queried_position: UVec2, queried_position: UVec2,
) -> bool { ) -> bool {
let Ok(item::Item {size, ..}) = item_query.get(queried_item) else { let Ok(item::Item {size, ..}) = item_query.get(queried_item) else {
error!("Could not query inventory child ({queried_item}), probably not item?");
return false; return false;
}; };
let children = contained_items.iter() let children = contained_items.iter()

View file

@ -20,7 +20,7 @@ const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
#[require(Sprite, InteractiveObject, Inventory)] #[require(Sprite, InteractiveObject, Inventory)]
pub struct Container; pub struct Container;
fn on_container_interact( pub fn on_container_interact(
event: On<InteractionEvent>, event: On<InteractionEvent>,
mut commands: Commands, mut commands: Commands,
locked_query: Query<(), With<Locked>>, locked_query: Query<(), With<Locked>>,
@ -42,7 +42,7 @@ pub fn container_bundle(
asset_server: &Res<AssetServer>, asset_server: &Res<AssetServer>,
position: Vec2, position: Vec2,
inventory_size: UVec2, inventory_size: UVec2,
items: Vec<Item> items: Vec<Item>,
) -> impl Bundle { ) -> impl Bundle {
let image = asset_server.load(CRATE_CLOSED_ASSET); let image = asset_server.load(CRATE_CLOSED_ASSET);
( (
@ -50,7 +50,6 @@ pub fn container_bundle(
Transform::from_xyz(position.x, position.y - meters(0.5), 0.), Transform::from_xyz(position.x, position.y - meters(0.5), 0.),
Sprite::from_image(image), Sprite::from_image(image),
Inventory::new(inventory_size), Inventory::new(inventory_size),
Observer::new(on_container_interact),
Children::spawn(( Children::spawn((
SpawnIter(items.into_iter()), SpawnIter(items.into_iter()),
Spawn(( Spawn((
@ -59,5 +58,6 @@ pub fn container_bundle(
Transform::from_xyz(0., meters(0.5), 0.), Transform::from_xyz(0., meters(0.5), 0.),
)), )),
)), )),
Name::new(format!("Container {}x{}", inventory_size.x, inventory_size.y)),
) )
} }

View file

@ -19,7 +19,7 @@ impl Default for Door {
} }
} }
fn on_door_interact( pub fn on_door_interact(
event: On<InteractionEvent>, event: On<InteractionEvent>,
mut commands: Commands, mut commands: Commands,
locked_query: Query<(), With<Locked>>, locked_query: Query<(), With<Locked>>,
@ -69,7 +69,6 @@ pub fn door_bundle(asset_server: &Res<AssetServer>, position: Vec2, facing_left:
Transform::from_xyz(position.x, position.y, 0.), Transform::from_xyz(position.x, position.y, 0.),
Name::new(format!("Door ({}, {})", position.x, position.y)), Name::new(format!("Door ({}, {})", position.x, position.y)),
door_collider(), door_collider(),
Observer::new(on_door_interact),
children![ children![
( (
Collider::cuboid(meters(0.5), meters(1.)), Collider::cuboid(meters(0.5), meters(1.)),

View file

@ -2,6 +2,7 @@ use bevy::prelude::*;
pub mod container; pub mod container;
pub mod door; pub mod door;
pub mod stairs;
pub mod systems; pub mod systems;
pub mod tilemap; pub mod tilemap;

98
src/layout/stairs.rs Normal file
View file

@ -0,0 +1,98 @@
use bevy::{ecs::relationship::RelatedSpawner, prelude::*};
use bevy_rapier2d::prelude::*;
use crate::{meters, player::Player};
use super::*;
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)]
#[require(InteractiveObject, Collider, Sensor)]
pub enum StairCollider {
#[default]
Up,
Down,
}
#[derive(Component, Clone, Copy, Default, Reflect, Debug, PartialEq)]
#[reflect(Component, Clone, Default, Debug, PartialEq)]
#[require(Transform)]
pub struct Stairs {
pub up: Option<Vec2>,
pub down: Option<Vec2>,
}
pub fn on_stairs_interact(
event: On<InteractionEvent>,
collider_query: Query<(&ChildOf, &StairCollider)>,
stairs_query: Query<&Stairs>,
mut player_query: Query<&mut Transform, With<Player>>,
) {
let Ok((parent, collider_type)) = collider_query.get(event.entity) else {
return;
};
let Ok(stairs) = stairs_query.get(parent.0) else {
error!("StairCollider does not have Stairs parent");
return;
};
let Ok(mut player_transform) = player_query.single_mut() else {
error!("Player is not single");
return;
};
let offset = match collider_type {
StairCollider::Up => {
let Some(offset) = stairs.up else {
error!("StairCollider::Up triggered but Stairs.up is None");
return;
};
offset
},
StairCollider::Down => {
let Some(offset) = stairs.down else {
error!("StairCollider::Down triggered but Stairs.down is None");
return;
};
offset
},
};
player_transform.translation.x += offset.x;
player_transform.translation.y += offset.y;
}
pub fn stairs_bundle(
_asset_server: &Res<AssetServer>,
position: Vec2,
up_position: Option<Vec2>,
down_position: Option<Vec2>,
) -> impl Bundle {
let (has_up, has_down) = (up_position.is_some(), down_position.is_some());
(
Stairs {
up: up_position,
down: down_position,
},
Transform::from_xyz(position.x, position.y, -1.),
Children::spawn(
SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| {
if has_up {
parent.spawn((
StairCollider::Up,
Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(-1.), 0., 0.),
));
}
if has_down {
parent.spawn((
StairCollider::Down,
Collider::cuboid(meters(1.), meters(1.)),
Transform::from_xyz(meters(1.), 0., 0.),
));
}
})
),
Name::new(format!("Stairs ({},{})", position.x, position.y))
)
}

View file

@ -115,7 +115,7 @@ pub fn setup_world(
vec2(meters(15.5), meters(-4.5))), vec2(meters(15.5), meters(-4.5))),
// 2F // 2F
(Collider::cuboid(meters(0.5), meters(1.5)), (Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(0.5), meters(0.5))), vec2(meters(0.5), meters(-0.5))),
(Collider::cuboid(meters(0.5), meters(1.5)), (Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(15.5), meters(-0.5))), vec2(meters(15.5), meters(-0.5))),
]; ];
@ -123,6 +123,25 @@ pub fn setup_world(
commands.spawn(door::door_bundle(&asset_server, vec2(meters(1.5), 0.), true)); commands.spawn(door::door_bundle(&asset_server, vec2(meters(1.5), 0.), true));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), 0.), false)); commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), 0.), false));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(5.5), 0.), false)).insert(Locked); commands.spawn(door::door_bundle(&asset_server, vec2(meters(5.5), 0.), false)).insert(Locked);
commands.spawn(container::container_bundle(&asset_server, vec2(meters(-2.), 0.), uvec2(8, 8), items)); commands.spawn(container::container_bundle(&asset_server, vec2(meters(-2.), 0.), uvec2(8, 8), items.clone()));
commands.spawn(container::container_bundle(&asset_server, vec2(meters(2.), meters(4.)), uvec2(10, 8), items.clone()));
commands.spawn(tilemap::tilemap_bundle(&asset_server, uvec2(16, 16), tiles, colliders)); commands.spawn(tilemap::tilemap_bundle(&asset_server, uvec2(16, 16), tiles, colliders));
commands.spawn(stairs::stairs_bundle(
&asset_server,
vec2(meters(-5.), 0.),
Some(vec2(meters(2.), meters(4.))),
None,
));
commands.spawn(stairs::stairs_bundle(
&asset_server,
vec2(meters(-5.), meters(4.)),
Some(vec2(meters(2.), meters(4.))),
Some(vec2(meters(-2.), meters(-4.))),
));
commands.spawn(stairs::stairs_bundle(
&asset_server,
vec2(meters(-5.), meters(8.)),
None,
Some(vec2(meters(-2.), meters(-4.))),
));
} }

View file

@ -99,6 +99,9 @@ impl Plugin for ExpeditionPlugin {
)) ))
.add_systems(OnEnter(GameState::Inventory), ui::inventory::systems::setup_ui_inventory) .add_systems(OnEnter(GameState::Inventory), ui::inventory::systems::setup_ui_inventory)
.add_systems(OnExit(GameState::Inventory), ui::inventory::systems::clear_ui_inventory) .add_systems(OnExit(GameState::Inventory), ui::inventory::systems::clear_ui_inventory)
.add_observer(ui::inventory::observers::on_ui_rotate); .add_observer(ui::inventory::observers::on_ui_rotate)
.add_observer(layout::container::on_container_interact)
.add_observer(layout::door::on_door_interact)
.add_observer(layout::stairs::on_stairs_interact);
} }
} }

View file

@ -18,7 +18,7 @@ pub struct Player {
impl Default for Player { impl Default for Player {
fn default() -> Self { fn default() -> Self {
Self { speed: meters(0.8) } Self { speed: meters(4.) }
} }
} }