feat: Display shop offers (#34)

This commit is contained in:
demenik
2025-12-01 15:13:48 +01:00
parent f908192c0e
commit 9842fe16d6
9 changed files with 116 additions and 12 deletions

View File

@@ -39,7 +39,7 @@ impl ItemType {
ItemType::Berry => { ItemType::Berry => {
"Von Pflanzen erntbar. Kann im Shop zum Einkaufen benutzt werden.".into() "Von Pflanzen erntbar. Kann im Shop zum Einkaufen benutzt werden.".into()
} }
ItemType::Shovel => "Schaltet ein neues Feld im Garten frei.".into(), ItemType::Shovel => "Im Shop kaufbar. Schaltet ein neues Feld im Garten frei. Preis steigt bei jedem Kauf!".into(),
ItemType::BerrySeed { name } => { ItemType::BerrySeed { name } => {
let seed_config = game_config.berry_seeds.iter().find(|s| s.name == *name); let seed_config = game_config.berry_seeds.iter().find(|s| s.name == *name);
if let Some(s) = seed_config { if let Some(s) = seed_config {
@@ -68,7 +68,11 @@ impl ItemType {
} }
} }
pub fn get_sprite(&self, asset_server: Res<AssetServer>, game_config: &GameConfig) -> AseSlice { pub fn get_sprite(
&self,
asset_server: &Res<AssetServer>,
game_config: &GameConfig,
) -> AseSlice {
match self { match self {
ItemType::Berry => AseSlice { ItemType::Berry => AseSlice {
name: "Berry".into(), name: "Berry".into(),

View File

@@ -20,12 +20,13 @@ fn buttons(
itemstack_query: Query<&ItemStack>, itemstack_query: Query<&ItemStack>,
root_query: Query<(Entity, &RootMarker)>, root_query: Query<(Entity, &RootMarker)>,
game_config: Res<GameConfig>, game_config: Res<GameConfig>,
asset_server: Res<AssetServer>,
) { ) {
for (interaction, button_type) in &mut interaction_query { for (interaction, button_type) in &mut interaction_query {
match *interaction { match *interaction {
Interaction::Pressed => match button_type { Interaction::Pressed => match button_type {
ButtonType::InventoryOpen => { ButtonType::InventoryOpen => {
open_inventory(&mut commands, itemstack_query, &game_config); open_inventory(&mut commands, itemstack_query, &game_config, &asset_server);
} }
ButtonType::InventoryClose => { ButtonType::InventoryClose => {
for (entity, root) in root_query.iter() { for (entity, root) in root_query.iter() {

View File

@@ -6,6 +6,7 @@ pub fn open_inventory(
commands: &mut Commands, commands: &mut Commands,
items: Query<&ItemStack>, items: Query<&ItemStack>,
game_config: &Res<GameConfig>, game_config: &Res<GameConfig>,
asset_server: &Res<AssetServer>,
) { ) {
commands commands
.spawn(( .spawn((
@@ -61,7 +62,7 @@ pub fn open_inventory(
}) })
.with_children(|parent| { .with_children(|parent| {
for itemstack in items.iter() { for itemstack in items.iter() {
parent.spawn(list_itemstack(itemstack, game_config)); parent.spawn(list_itemstack(itemstack, game_config, asset_server));
} }
}); });
}); });

View File

@@ -1,6 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
pub fn list_itemstack(itemstack: &ItemStack, game_config: &GameConfig) -> impl Bundle { pub fn list_itemstack(
itemstack: &ItemStack,
game_config: &GameConfig,
asset_server: &Res<AssetServer>,
) -> impl Bundle {
let name = match itemstack.amount { let name = match itemstack.amount {
1 => itemstack.item_type.singular(game_config), 1 => itemstack.item_type.singular(game_config),
_ => itemstack.item_type.plural(game_config), _ => itemstack.item_type.plural(game_config),
@@ -8,21 +12,22 @@ pub fn list_itemstack(itemstack: &ItemStack, game_config: &GameConfig) -> impl B
( (
Node { Node {
width: percent(100),
padding: UiRect::all(px(4)), padding: UiRect::all(px(4)),
..Node::hstack(px(8)) ..Node::hstack(px(8))
}, },
BackgroundColor(ButtonVariant::Secondary.normal_background()),
BorderRadius::all(px(10)), BorderRadius::all(px(10)),
children![ children![
( (
// Placeholder for icon
Node { Node {
height: percent(100), height: percent(100),
aspect_ratio: Some(1.0), aspect_ratio: Some(1.0),
..default() ..default()
}, },
BackgroundColor(ButtonVariant::Secondary.hover_background()), BackgroundColor(ButtonVariant::Secondary.hover_background()),
BorderRadius::all(px(10)) BorderRadius::all(px(10)),
itemstack.item_type.get_sprite(asset_server, game_config),
ImageNode::default()
), ),
( (
Node { Node {

View File

@@ -7,7 +7,9 @@ pub enum RootMarker {
#[derive(Component)] #[derive(Component)]
pub enum ButtonType { pub enum ButtonType {
ShopOpen,
ShopClose, ShopClose,
ShopBuyItem(ShopOffer),
} }
#[derive(Clone)] #[derive(Clone)]

View File

@@ -1,4 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use components::*;
use ui::open_shop;
pub mod components; pub mod components;
pub mod ui; pub mod ui;
@@ -6,5 +8,34 @@ pub mod ui;
pub struct ShopPlugin; pub struct ShopPlugin;
impl Plugin for ShopPlugin { impl Plugin for ShopPlugin {
fn build(&self, app: &mut App) {} fn build(&self, app: &mut App) {
app.add_systems(Update, buttons.run_if(in_state(AppState::GameScreen)));
}
}
fn buttons(
mut commands: Commands,
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
root_query: Query<(Entity, &RootMarker)>,
game_config: Res<GameConfig>,
asset_server: Res<AssetServer>,
) {
for (interaction, button_type) in &mut interaction_query {
match *interaction {
Interaction::Pressed => match button_type {
ButtonType::ShopOpen => {
open_shop(&mut commands, &game_config, &asset_server);
}
ButtonType::ShopClose => {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Shop => commands.entity(entity).despawn(),
}
}
}
_ => {}
},
_ => {}
}
}
} }

View File

@@ -1,3 +1,5 @@
pub mod offer;
pub mod shop; pub mod shop;
pub use offer::shop_offer;
pub use shop::open_shop; pub use shop::open_shop;

View File

@@ -0,0 +1,47 @@
use super::super::components::*;
use crate::{features::inventory::ui::item::list_itemstack, prelude::*};
pub fn shop_offer(
offer: &ShopOffer,
game_config: &GameConfig,
asset_server: &Res<AssetServer>,
) -> impl Bundle {
button(
ButtonType::ShopBuyItem(offer.clone()),
ButtonVariant::Secondary,
Node::default(),
|_| {
(
Node {
width: percent(100),
align_items: AlignItems::Center,
..Node::hstack(px(10))
},
children![
list_itemstack(&offer.item, game_config, asset_server),
shop_price(offer.cost, asset_server, game_config)
],
)
},
)
}
pub fn shop_price(
price: u32,
asset_server: &Res<AssetServer>,
game_config: &GameConfig,
) -> impl Bundle {
(
Node {
align_items: AlignItems::Center,
..Node::hstack(px(0))
},
children![
text(price.to_string(), 12.0, Color::WHITE),
(
ImageNode::default(),
ItemType::Berry.get_sprite(asset_server, game_config)
)
],
)
}

View File

@@ -1,7 +1,14 @@
use super::super::components::*; use super::super::components::*;
use crate::prelude::*; use crate::{features::shop::ui::shop_offer, prelude::*};
pub fn open_shop(
commands: &mut Commands,
game_config: &GameConfig,
asset_server: &Res<AssetServer>,
) {
// TODO: calculate tile_count
let offers = ShopOffer::list_all(game_config, 0);
pub fn open_shop(commands: &mut Commands) {
commands commands
.spawn(( .spawn((
RootMarker::Shop, RootMarker::Shop,
@@ -47,7 +54,11 @@ pub fn open_shop(commands: &mut Commands) {
], ],
)); ));
parent.spawn(Node::vstack(px(10))).with_children(|_| {}); parent.spawn(Node::vstack(px(10))).with_children(|parent| {
for offer in offers {
parent.spawn(shop_offer(&offer, game_config, asset_server));
}
});
}); });
}); });
} }