Skip to content
Snippets Groups Projects
Verified Commit 75a10045 authored by Louis's avatar Louis :fire:
Browse files

Actual trading with actual inventories

parent b13c3b84
No related branches found
No related tags found
No related merge requests found
Pipeline #251 failed with stages
in 3 minutes and 41 seconds
...@@ -4,6 +4,7 @@ use iyes_loopless::prelude::ConditionSet; ...@@ -4,6 +4,7 @@ use iyes_loopless::prelude::ConditionSet;
use crate::system::flow::AppState; use crate::system::flow::AppState;
mod sync_stats; mod sync_stats;
mod sync_trade;
mod sync_travel; mod sync_travel;
pub struct UISyncPlugin; pub struct UISyncPlugin;
...@@ -11,15 +12,18 @@ impl Plugin for UISyncPlugin { ...@@ -11,15 +12,18 @@ impl Plugin for UISyncPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<UITravelInfo>() app.init_resource::<UITravelInfo>()
.init_resource::<UIStatsData>() .init_resource::<UIStatsData>()
.init_resource::<UITradeData>()
.add_system_set( .add_system_set(
ConditionSet::new() ConditionSet::new()
.run_in_state(AppState::InGame) .run_in_state(AppState::InGame)
.with_system(sync_travel::sync_player_travel_info_to_ui) .with_system(sync_travel::sync_player_travel_info_to_ui)
.with_system(sync_stats::sync_player_stats_info_to_ui) .with_system(sync_stats::sync_player_stats_info_to_ui)
.with_system(sync_trade::sync_ui_trade_data)
.into(), .into(),
); );
} }
} }
pub use sync_stats::UIStatsData; pub use sync_stats::UIStatsData;
pub use sync_trade::{ItemTradeData, UITradeData};
pub use sync_travel::UITravelInfo; pub use sync_travel::UITravelInfo;
use std::collections::HashMap;
use bevy::asset::{Assets, Handle};
use bevy::prelude::{Entity, Query, Res, ResMut, Resource, With};
use crate::const_data::{get_goods_from_name, get_goods_from_name_checked, TRADE_GOODS};
use crate::states::Player;
use crate::world::{
CurrentResidence, TownName, TradeGood, TradeManifest, TradingState, TravelTarget,
};
#[derive(Clone)]
pub struct ItemTradeData {
pub definition: TradeGood,
pub current_cost: usize,
pub town_amount: usize,
pub player_amount: usize,
}
#[derive(Resource, Default)]
pub struct UITradeData {
pub town_gold: usize,
pub town_entity: Option<Entity>,
pub player_gold: usize,
pub player_entity: Option<Entity>,
pub shop_items: Vec<ItemTradeData>,
}
pub fn sync_ui_trade_data(
player_query: Query<
(
Entity,
&TradingState,
Option<&TravelTarget>,
&CurrentResidence,
),
With<Player>,
>,
town_query: Query<(Entity, &TownName, &TradingState, &Handle<TradeManifest>)>,
manifests: Res<Assets<TradeManifest>>,
mut trade_data: ResMut<UITradeData>,
) {
let mut trade_entries = HashMap::with_capacity(15);
let (entity, player_trading_state, target, residence) = match player_query.get_single() {
Ok(p) => p,
Err(_) => return,
};
trade_data.player_entity = Some(entity);
trade_data.player_gold = player_trading_state.gold.max(0) as usize;
let town_name = target
.map(|target| target.0.clone())
.unwrap_or_else(|| match residence {
CurrentResidence::RestingAt(place) => place.clone(),
CurrentResidence::TravellingFrom(..) => String::new(),
});
let (town_entity, _, town_trading_state, manifest_handle) = match town_query
.into_iter()
.find(|(_, name, _, _)| town_name == *name.0)
{
Some(v) => v,
None => {
trade_data.shop_items = vec![];
trade_data.town_entity = None;
return;
}
};
trade_data.town_entity = Some(town_entity);
trade_data.town_gold = town_trading_state.gold.max(0) as usize;
for (item_name, trade_good) in TRADE_GOODS.iter() {
let manifest = match manifests.get(manifest_handle) {
Some(m) => m,
None => continue,
};
let roster_entry = match manifest.get(&trade_good.name) {
Some(e) => e,
None => continue,
};
let town_amount = match town_trading_state.items.get(&trade_good.name) {
Some(q) => *q,
None => 0,
};
let player_amount = match player_trading_state.items.get(&trade_good.name) {
Some(q) => *q,
None => 0,
};
let mut cost_multiplier = -1.0;
for (limit, multiplier) in &roster_entry.cost_multipliers {
cost_multiplier = *multiplier;
if town_amount < *limit {
break;
}
}
let current_fractional_cost = cost_multiplier * trade_good.gold_value as f32;
let current_cost = current_fractional_cost.ceil() as usize;
trade_entries.insert(
item_name.clone(),
ItemTradeData {
definition: trade_good.clone(),
current_cost,
town_amount,
player_amount,
},
);
}
let mut entries = trade_entries
.into_values()
.filter(|listing| listing.town_amount > 0 || listing.player_amount > 0)
.collect::<Vec<ItemTradeData>>();
entries.sort_by(|a, b| a.definition.name.cmp(&b.definition.name));
trade_data.shop_items = entries;
}
...@@ -164,7 +164,7 @@ pub mod context { ...@@ -164,7 +164,7 @@ pub mod context {
use crate::register_widget; use crate::register_widget;
use crate::ui::components::*; use crate::ui::components::*;
use crate::ui::sync::{UIStatsData, UITravelInfo}; use crate::ui::sync::{UIStatsData, UITradeData, UITravelInfo};
use crate::ui::widgets::*; use crate::ui::widgets::*;
use crate::world::EncounterState; use crate::world::EncounterState;
...@@ -246,7 +246,7 @@ pub mod context { ...@@ -246,7 +246,7 @@ pub mod context {
widget_context, widget_context,
ShopPanelProps, ShopPanelProps,
EmptyState, EmptyState,
UITravelInfo, UITradeData,
render_shop_panel render_shop_panel
); );
register_widget_with_resource!( register_widget_with_resource!(
......
...@@ -8,78 +8,74 @@ use kayak_ui::widgets::{ ...@@ -8,78 +8,74 @@ use kayak_ui::widgets::{
use crate::states::Player; use crate::states::Player;
use crate::ui::components::*; use crate::ui::components::*;
use crate::ui::prelude::*; use crate::ui::prelude::*;
use crate::ui::sync::UIStatsData; use crate::ui::sync::{ItemTradeData, UIStatsData, UITradeData};
use crate::ui::widgets::*; use crate::ui::widgets::*;
use crate::world::{CurrentResidence, MapQuery, TownPaths}; use crate::world::{CurrentResidence, MapQuery, TownName, TownPaths, TradeGood, TradingState};
use crate::{basic_widget, empty_props, on_button_click}; use crate::{basic_widget, empty_props, on_button_click};
empty_props!(ShopPanelProps); empty_props!(ShopPanelProps);
basic_widget!(ShopPanelProps => ShopPanel); basic_widget!(ShopPanelProps => ShopPanel);
pub fn buysell_button_factory(target: String) -> OnEvent { pub fn buysell_button_factory(target: String, is_buy: bool) -> OnEvent {
let target = target.clone(); let target = target.clone();
on_button_click!( on_button_click!(
ParamSet<( ParamSet<(Query<&mut TradingState>, Res<UITradeData>,)>,
Commands, |mut params: ParamSet<(Query<&mut TradingState>, Res<UITradeData>,)>| {
Res<TownPaths>, let player_entity = params.p1().player_entity.clone();
Query<(Entity, &CurrentResidence), With<Player>>, let town_entity = params.p1().town_entity.clone();
MapQuery, let entry = {
)>, let shop_items = &params.p1().shop_items;
|mut params: ParamSet<( shop_items
Commands, .iter()
Res<TownPaths>, .find(|en| en.definition.name == target)
Query<(Entity, &CurrentResidence), With<Player>>, .cloned()
MapQuery,
)>| {
let target = target.clone();
let (entity, current) = {
match params.p2().get_single() {
Ok((entity, current)) => (entity.clone(), (current.get_location()).clone()),
_ => return,
}
};
let places = match params.p1().routes.get(&current) {
Some(places) => places.clone(),
None => return,
}; };
let bundle = match params.p3().get_active_level() { if let (Some(player), Some(town), Some(entry)) = (player_entity, town_entity, entry) {
Some(level) => places.create_route_bundle_for(target, level).unwrap(), let mut query = params.p0();
None => return,
};
params.p0().entity(entity).insert(bundle); if query.contains(player) && query.contains(town) {
let mut has_purchased = false;
if is_buy {
if let Ok(mut player_inv) = query.get_mut(player) {
has_purchased =
player_inv.try_buy_items(entry.current_cost, target.clone(), 1);
}
if has_purchased {
if let Ok(mut town_inv) = query.get_mut(town) {
town_inv.try_sell_items(entry.current_cost, target.clone(), 1);
}
}
} else {
let adjusted_sell_price = adjust_player_sell_price(entry.current_cost);
if let Ok(mut town_inv) = query.get_mut(town) {
has_purchased =
town_inv.try_buy_items(adjusted_sell_price, target.clone(), 1);
}
if has_purchased {
if let Ok(mut player_inv) = query.get_mut(player) {
player_inv.try_sell_items(adjusted_sell_price, target.clone(), 1);
}
}
}
}
}
} }
) )
} }
fn adjust_player_sell_price(base: usize) -> usize {
(base as f32 * 0.8).ceil() as usize
}
pub fn render_shop_panel( pub fn render_shop_panel(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
query: Query<&ShopPanelProps>, query: Query<&ShopPanelProps>,
ui_data: Res<UIStatsData>, ui_data: Res<UITradeData>,
) -> bool { ) -> bool {
let parent_id = Some(entity); let parent_id = Some(entity);
let items = vec![
("Armour", 12usize, 12, 14, 2),
("Weapons", 1, 13, 14, 2),
("Luxury Goods", 13, 27, 14, 2),
("Ale", 11, 6, 14, 2),
("Milk", 2, 5, 14, 2),
("Cheese", 3, 6, 14, 2),
("Spare Wheel", 28, 125, 14, 2),
("Berries", 27, 4, 14, 2),
("Corn", 26, 5, 14, 2),
("Tomato", 25, 6, 14, 2),
("Chili", 24, 8, 14, 2),
("Grapes", 23, 9, 14, 2),
("Wheat", 22, 3, 14, 2),
];
log::info!("{:?}", items);
if let Ok(props) = query.get(entity) { if let Ok(props) = query.get(entity) {
rsx! { rsx! {
<ElementBundle> <ElementBundle>
...@@ -106,31 +102,37 @@ pub fn render_shop_panel( ...@@ -106,31 +102,37 @@ pub fn render_shop_panel(
..Default::default() ..Default::default()
} } styles={KStyle { pointer_events: value(PointerEvents::None), padding: edge_px(5.0), ..Default::default() }}> } } styles={KStyle { pointer_events: value(PointerEvents::None), padding: edge_px(5.0), ..Default::default() }}>
{ {
for (name, idx, cost, shop_amount, player_amount) in items.iter() { for ItemTradeData {
constructor! { town_amount,
<BackgroundBundle player_amount,
styles={KStyle { current_cost,
pointer_events: value(PointerEvents::ChildrenOnly), definition: TradeGood { name, icon, .. },
layout_type: value(LayoutType::Row), } in &ui_data.shop_items
col_between: stretch(0.5), {
height: px(40.0), constructor! {
bottom: px(15.0), <BackgroundBundle
padding_top: px(4.0), styles={KStyle {
padding_bottom: px(4.0), pointer_events: value(PointerEvents::ChildrenOnly),
color: value(Color::BLACK), layout_type: value(LayoutType::Row),
..Default::default() col_between: stretch(0.5),
}} height: px(40.0),
> bottom: px(15.0),
<InsetIconWidget props={InsetIconProps { image: IconContent::Atlas(String::from("icons"), *idx), size: 32.0}} /> padding_top: px(4.0),
<TextWidgetBundle styles={KStyle { width: stretch(1.5), ..Default::default() }} text={TextProps { content: String::from(*name), size: 30.0, line_height: Some(32.0), ..Default::default() } }/> padding_bottom: px(4.0),
<TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}g", cost), size: 30.0, line_height: Some(32.0), ..Default::default() } }/> color: value(Color::BLACK),
<TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", shop_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/> ..Default::default()
<TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", player_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/> }}
<ButtonWidget props={ButtonWidgetProps { font_size: 20.0, text: String::from("Buy"), ..Default::default() }} /> >
<ButtonWidget props={ButtonWidgetProps { font_size: 20.0, text: String::from("Sell"), ..Default::default() }} /> <InsetIconWidget props={InsetIconProps { image: icon.clone(), size: 32.0}} />
</BackgroundBundle> <TextWidgetBundle styles={KStyle { width: stretch(1.5), ..Default::default() }} text={TextProps { content: name.clone(), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
}; <TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}g", current_cost), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
} <TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", town_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
<TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", player_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
<ButtonWidget on_event={buysell_button_factory(name.clone(), true)} props={ButtonWidgetProps { font_size: 20.0, is_disabled: ui_data.player_gold < *current_cost || town_amount == &0, text: String::from("Buy"), ..Default::default() }} />
<ButtonWidget on_event={buysell_button_factory(name.clone(), false)} props={ButtonWidgetProps { font_size: 20.0, is_disabled: ui_data.town_gold < adjust_player_sell_price(*current_cost) || player_amount == &0, text: String::from("Sell"), ..Default::default() }} />
</BackgroundBundle>
}
}
} }
</ScrollBoxBundle> </ScrollBoxBundle>
</ScrollContextProviderBundle> </ScrollContextProviderBundle>
......
...@@ -158,7 +158,6 @@ pub fn render_town_menu_panel( ...@@ -158,7 +158,6 @@ pub fn render_town_menu_panel(
{ {
log::info!("Town menu is showing: {:?}", &state.tab);
match state.tab { match state.tab {
TownMenuTab::Travel => { TownMenuTab::Travel => {
constructor! { constructor! {
......
...@@ -30,6 +30,7 @@ pub struct TradeGood { ...@@ -30,6 +30,7 @@ pub struct TradeGood {
pub name: String, pub name: String,
pub icon: IconContent, pub icon: IconContent,
pub food_value: usize, pub food_value: usize,
pub gold_value: usize,
} }
impl PartialEq for TradeGood { impl PartialEq for TradeGood {
...@@ -124,7 +125,7 @@ impl TradingState { ...@@ -124,7 +125,7 @@ impl TradingState {
if self.gold < amount.as_() { if self.gold < amount.as_() {
false false
} else { } else {
self.adjust_gold(amount); self.adjust_gold(-amount.as_());
true true
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment