feat: Layout changes

- Added padlock layout object
- Added lockpick item
- Added ItemImage component
- Tilemap colliders are now creating from the tiles themselves
- Removed items from container bundle
- Removed colliders from tilemap bundle
- Removed setup_player system
This commit is contained in:
Alexey 2026-03-25 13:32:12 +03:00
commit 42cfd14214
10 changed files with 175 additions and 57 deletions

View file

@ -7,7 +7,6 @@ use crate::{
inventory::{
ActiveInventory,
Inventory,
item::Item,
}
};
@ -42,7 +41,6 @@ pub fn container_bundle(
asset_server: &Res<AssetServer>,
position: Vec2,
inventory_size: UVec2,
items: Vec<Item>,
) -> impl Bundle {
let image = asset_server.load(CRATE_CLOSED_ASSET);
(
@ -51,7 +49,6 @@ pub fn container_bundle(
Sprite::from_image(image),
Inventory::new(inventory_size),
Children::spawn((
SpawnIter(items.into_iter()),
Spawn((
Collider::cuboid(meters(1.), meters(1.)),
Sensor,

66
src/layout/lock.rs Normal file
View file

@ -0,0 +1,66 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use crate::{inventory::{Inventory, item::lockpick::Lockpick}, meters, player::Player};
use super::*;
const PADLOCK_IMAGE_PATH: &'static str = "sprites/interactive/padlock.png";
#[derive(Component, Debug, PartialEq, Eq, Default, Clone, Copy, Reflect)]
#[reflect(Component, Debug, PartialEq, Default, Clone)]
#[require(InteractiveObject)]
pub struct Padlock;
pub fn on_padlock_interaction(
event: On<InteractionEvent>,
mut commands: Commands,
query: Query<(&ChildOf, Entity), With<Padlock>>,
inventory_query: Query<(&Children, &ChildOf), With<Inventory>>,
player_query: Query<(), With<Player>>,
lockpick_query: Query<(), With<Lockpick>>,
) {
let Ok((parent, lock_id)) = query.get(event.entity) else {
return;
};
// find lockpick in player inventory
let mut lockpick_id = None;
for (items, inventory_parent) in inventory_query {
if player_query.get(inventory_parent.0).is_err() {
continue;
}
for item_id in items {
if lockpick_query.get(*item_id).is_ok() {
lockpick_id = Some(*item_id);
break;
}
}
};
let Some(lockpick_id) = lockpick_id else {
return;
};
commands.entity(parent.0).remove::<Locked>();
commands.entity(lock_id).despawn();
commands.entity(lockpick_id).despawn();
}
pub fn padlock_bundle(asset_server: &Res<AssetServer>, facing_left: bool) -> impl Bundle {
let image = asset_server.load(PADLOCK_IMAGE_PATH);
let sign = if facing_left { -1. } else { 1. };
(
Padlock,
Sprite {
image,
flip_x: facing_left,
..default()
},
Transform::from_xyz(meters(sign * 0.125), meters(0.), 0.),
Children::spawn_one((
Transform::from_xyz(meters(sign * 0.1875), 0., 0.),
Collider::cuboid(meters(0.1875), meters(1.)),
Sensor,
)),
)
}

View file

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

View file

@ -2,12 +2,16 @@ use bevy::{
ecs::query::QueryFilter,
prelude::*,
};
use bevy_rapier2d::prelude::*;
use crate::{
meters,
inventory::item::Item,
player::Player,
inventory::item::lockpick::lockpick_bundle,
player::{
Player,
player_bundle,
},
};
use super::*;
@ -61,20 +65,19 @@ pub fn detect_interact_collisions(
}
}
pub fn lock_door(
mut commands: Commands,
query: Query<&ChildOf, Added<lock::Padlock>>,
) {
for parent in query {
commands.entity(parent.0).insert(Locked);
}
}
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)),
];
// floor 1F
let mut tiles = (0..16).map(|x| {
(0, uvec2(x, 1))
@ -102,46 +105,53 @@ pub fn setup_world(
// wall connectors
tiles.extend([
(13, 4), (11, 4), (9, 4),
(4, 8), (4, 4), (11, 8), (11, 4),
].iter().map(|(x, y)| {
(2, uvec2(*x, *y))
}));
let colliders: Vec<(Collider, Vec2)> = vec![
// 1F
(Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(0.5), meters(-4.5))),
(Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(15.5), meters(-4.5))),
// 2F
(Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(0.5), meters(-0.5))),
(Collider::cuboid(meters(0.5), meters(1.5)),
vec2(meters(15.5), meters(-0.5))),
];
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(5.5), 0.), false)).insert(Locked);
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, 10), tiles));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(-3.5), 0.), false));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(-3.5), meters(4.)), false))
.with_child(lock::padlock_bundle(&asset_server, false));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), 0.), true))
.with_child(lock::padlock_bundle(&asset_server, true));
commands.spawn(door::door_bundle(&asset_server, vec2(meters(3.5), meters(4.)), true));
commands.spawn(stairs::stairs_bundle(
&asset_server,
vec2(meters(-5.), 0.),
vec2(meters(0.), 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.)),
vec2(meters(0.), meters(4.)),
None,
Some(vec2(meters(-2.), meters(-4.))),
));
commands.spawn(container::container_bundle(
&asset_server,
vec2(meters(-6.), 0.),
uvec2(1, 1),
));
commands.spawn(container::container_bundle(
&asset_server,
vec2(meters(-6.), meters(4.)),
uvec2(1, 1),
)).with_child(lockpick_bundle(&asset_server, UVec2::ZERO));
commands.spawn(container::container_bundle(
&asset_server,
vec2(meters(6.), 0.),
uvec2(1, 1),
));
commands.spawn(container::container_bundle(
&asset_server,
vec2(meters(6.), meters(4.)),
uvec2(1, 1),
)).with_child(lockpick_bundle(&asset_server, UVec2::ZERO));
commands.spawn(player_bundle(&asset_server, vec2(meters(-6.), 0.)));
}

View file

@ -25,7 +25,6 @@ pub fn tilemap_bundle(
asset_server: &Res<AssetServer>,
size: UVec2,
tiles: Vec<(u16, UVec2)>,
colliders: Vec<(Collider, Vec2)>,
) -> impl Bundle {
let tile_data: Vec<Option<TileData>> = (0..size.element_product()).map(|xy| {
if let Some((id, _)) = tiles.iter().find(|(_, pos)| pos.x == xy % size.x && pos.y == xy / size.x) {
@ -47,15 +46,25 @@ pub fn tilemap_bundle(
},
Transform::from_xyz(0., meters(0.5) * size.y as f32 - meters(3.), 0.),
TilemapChunkTileData(tile_data),
Children::spawn(SpawnIter(colliders.into_iter()
.map(move |(collider, pos)| {(
collider.clone(),
Children::spawn(SpawnIter(tiles.into_iter().map(move |(_, pos)| {(
// TODO: optimize colliders
Collider::cuboid(meters(0.5), meters(0.5)),
Transform::from_xyz(
pos.x - meters(0.5) * size.x as f32,
pos.y,
0.
meters(0.5 + pos.x as f32 - (size.x as f32 * 0.5)),
meters(0.5 + pos.y as f32 - (size.y as f32 * 0.5)),
0.,
),
)})
)),
//Children::spawn(SpawnIter(colliders.into_iter()
// .map(move |(collider, pos)| {(
// collider.clone(),
// Transform::from_xyz(
// pos.x - meters(0.5) * size.x as f32,
// pos.y - meters(0.5) * size.y as f32,
// 0.
// ),
// )})
//)),
)
}