diff --git a/.gitignore b/.gitignore index 044cdb598b05139bbaba6c6d2ca89c6f1d0de8d5..395679459596ccf00f20e10590691ed765d3c6bc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ .idea/ .vscode/ dist/ -artifacts/ \ No newline at end of file +artifacts/ + +*.ods# \ No newline at end of file diff --git a/assets/encounters/grasslands.encounters.toml b/assets/encounters/grasslands.encounters.toml new file mode 100644 index 0000000000000000000000000000000000000000..3f51b782ea06629e9ea2c9104de1c2d30d299bc6 --- /dev/null +++ b/assets/encounters/grasslands.encounters.toml @@ -0,0 +1,16 @@ +[[encounters]] +chance = 90 +title = "You hear a rustle in the bushes" +description = """ + +""" + +[[encounters]] +chance = 10 +title = "You see a small shrine" +description = """ +Off to the side of the path is located a small offering shrine to some forgotten god, chipped and covered in moss. + +Around the base of the shrine, you see some small totems and other miscellanious offerings. Do you want to leave anything for these ancient gods?""" + +[[encounters.options]] \ No newline at end of file diff --git a/assets/resources.apack b/assets/resources.apack index c2669d0b236ff87a757b1a5f33cff77febb1dbfc..3ad723412d4cdd5561d095a77ba3bb17e1554d75 100644 --- a/assets/resources.apack +++ b/assets/resources.apack @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7d3705a1bcb0534de927d60743ca9e4f33352bbefeaf1618d6eb91f102639fc -size 1930588 +oid sha256:08fbd17915eea98e0e3e077c32249781bb0b33d821aeab9c9dac5196eaab6527 +size 1955812 diff --git a/assets/trade_manifests/monastery.manifest.toml b/assets/trade_manifests/monastery.manifest.toml index e909957b5cd813175998e5fd07eb7d661cf5a3d3..88bc5033b02cde1a5f7d4e63bf2d3f91aec36cd0 100644 --- a/assets/trade_manifests/monastery.manifest.toml +++ b/assets/trade_manifests/monastery.manifest.toml @@ -61,16 +61,15 @@ cost_multipliers = [ ] [Milk] -tick_decay = 0.25 -natural_limit = 0 +tick_decay = 1 +natural_limit = 15 cost_multipliers = [ [5, 1.4], [9, 1.25], - [10, 1], - [15, 0.8], - [30, 0.7], - [99999, 0.6] + [30, 1.1], + [99999, 1] ] +oversupply_trend = "halt" [Cheese] tick_decay = 0.25 @@ -97,16 +96,15 @@ cost_multipliers = [ ] [Corn] -tick_decay = 0.25 -natural_limit = 0 +tick_decay = 1.5 +natural_limit = 30 cost_multipliers = [ [5, 1.4], [9, 1.25], - [10, 1], - [15, 0.8], - [30, 0.7], - [99999, 0.6] + [30, 1.1], + [99999, 1] ] +oversupply_trend = "halt" [Tomato] tick_decay = 0.25 @@ -133,25 +131,34 @@ cost_multipliers = [ ] [Grapes] -tick_decay = 0.25 +tick_decay = 1.5 +natural_limit = 30 +cost_multipliers = [ + [5, 1.4], + [9, 1.25], + [30, 1.1], + [99999, 1] +] +oversupply_trend = "halt" + +[Wine] +tick_decay = 0.0 natural_limit = 0 cost_multipliers = [ [5, 1.4], [9, 1.25], - [10, 1], - [15, 0.8], - [30, 0.7], - [99999, 0.6] + [30, 1.1], + [99999, 1] ] +oversupply_trend = "halt" [Wheat] -tick_decay = 0.25 -natural_limit = 0 +tick_decay = 1.5 +natural_limit = 30 cost_multipliers = [ [5, 1.4], [9, 1.25], - [10, 1], - [15, 0.8], - [30, 0.7], - [99999, 0.6] + [30, 1.1], + [99999, 1] ] +oversupply_trend = "halt" \ No newline at end of file diff --git a/assets/trade_manifests/ship.manifest.toml b/assets/trade_manifests/ship.manifest.toml new file mode 100644 index 0000000000000000000000000000000000000000..14e7eea09ceef1785f8cef74e2536f22dbc385f9 --- /dev/null +++ b/assets/trade_manifests/ship.manifest.toml @@ -0,0 +1,8 @@ +[Bread] +tick_decay = 50.0 +natural_limit = 50 +cost_multipliers = [ + [99999, 0] +] +oversupply_trend = "halt" +undersupply_trend = "produce" \ No newline at end of file diff --git a/assets/trade_manifests/whitestone.manifest.toml b/assets/trade_manifests/whitestone.manifest.toml index dec88c9b36c865d7f0cebc0464a02459205dea46..9beb037c10f2b7e1bc2e9db81ac18abe0ac5a019 100644 --- a/assets/trade_manifests/whitestone.manifest.toml +++ b/assets/trade_manifests/whitestone.manifest.toml @@ -167,3 +167,26 @@ cost_multipliers = [ [30, 0.7], [99999, 0.6] ] + +[Bread] +tick_decay = 0.25 +natural_limit = 0 +cost_multipliers = [ + [5, 1.4], + [9, 1.25], + [10, 1], + [15, 0.8], + [30, 0.7], + [99999, 0.6] +] + +[Wine] +tick_decay = 0.0 +natural_limit = 0 +cost_multipliers = [ + [5, 1.8], + [9, 1.6], + [30, 1.4], + [99999, 1] +] +oversupply_trend = "halt" \ No newline at end of file diff --git a/game_core/src/assets/startup.rs b/game_core/src/assets/startup.rs index 5c607ed230fcd210435be99f527502875eb2c50d..74d4a5bcdc7813fa7dc8343a20cce5a9ac15fbfe 100644 --- a/game_core/src/assets/startup.rs +++ b/game_core/src/assets/startup.rs @@ -18,6 +18,7 @@ pub fn start_load_resources(mut loader: AssetTypeLoader) { loader.load_trade_manifest(&[ ("trade_manifests/whitestone.manifest.toml", "whitestone"), ("trade_manifests/monastery.manifest.toml", "monastery"), + ("trade_manifests/ship.manifest.toml", "ship"), ]); } diff --git a/game_core/src/persistance/fs_utils.rs b/game_core/src/persistance/fs_utils.rs index f5b9a76f21a8750cc8f7ac53a78a767e63b0159c..6130e2ac4bbacaaece130ca4e8aabb75c1f69c2f 100644 --- a/game_core/src/persistance/fs_utils.rs +++ b/game_core/src/persistance/fs_utils.rs @@ -34,7 +34,21 @@ pub fn read_config_file() -> Option<Options> { pub fn save_config_file(options: Options) -> Option<()> { let root_dir = get_root_save_dir()?; + std::fs::create_dir_all(&root_dir) + .inspect_err(|e| { + log::error!("{}", e); + }) + .ok()?; let save_path = root_dir.join("config.toml"); - std::fs::write(save_path, toml::to_string_pretty(&options).ok()?).ok() + std::fs::write( + save_path, + toml::to_string_pretty(&options) + .inspect_err(|e| { + log::error!("{}", e); + }) + .ok()?, + ) + .map(|()| log::info!("SAVED")) + .ok() } diff --git a/game_core/src/persistance/save_file.rs b/game_core/src/persistance/save_file.rs index 681702d2351b8eb64bcc2d15f5f6dcf8ebbce591..65d7cf2a7e00bd1be90877876644b2bda9a6e6c7 100644 --- a/game_core/src/persistance/save_file.rs +++ b/game_core/src/persistance/save_file.rs @@ -11,11 +11,19 @@ use crate::persistance::fs_utils::{get_root_save_dir, AUTOSAVE_NAME}; use crate::states::Player; use crate::system::flow::AppState; use crate::world::{ - ActiveLevel, CurrentResidence, DistanceTravelled, EncounterState, HungerState, + ActiveLevel, CraftSpecialism, CurrentResidence, DistanceTravelled, EncounterState, HungerState, PendingLoadState, TownName, TradeManifestTickState, TradingState, TravelPath, TravelTarget, }; -pub type TownPersistenceData = HashMap<String, (TradingState, HungerState, TradeManifestTickState)>; +pub type TownPersistenceData = HashMap< + String, + ( + TradingState, + HungerState, + TradeManifestTickState, + Option<CraftSpecialism>, + ), +>; #[derive(Serialize, Deserialize, Debug, Resource)] pub struct PersistenceState { @@ -80,16 +88,22 @@ pub fn sync_state_to_persistence( &TradingState, &HungerState, &TradeManifestTickState, + Option<&CraftSpecialism>, )>, encounters: Res<EncounterState>, active_map: Res<ActiveLevel>, ) { let town_states = town_query .iter() - .map(|(name, trading, hunger, manifest)| { + .map(|(name, trading, hunger, manifest, maybe_spec)| { ( name.0.clone(), - (trading.clone(), hunger.clone(), manifest.clone()), + ( + trading.clone(), + hunger.clone(), + manifest.clone(), + maybe_spec.cloned(), + ), ) }) .collect::<TownPersistenceData>(); diff --git a/game_core/src/ui/sync/sync_trade.rs b/game_core/src/ui/sync/sync_trade.rs index 7cd58dd4a043a01913f99f0adad2d86118cb27e1..74561248fe038b98fb5af478213521bde4678c0b 100644 --- a/game_core/src/ui/sync/sync_trade.rs +++ b/game_core/src/ui/sync/sync_trade.rs @@ -6,7 +6,8 @@ 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, HungerState, TownName, TradeGood, TradeManifest, TradingState, TravelTarget, + CurrentResidence, HungerState, StarvationMarker, TownName, TradeGood, TradeManifest, + TradingState, TravelTarget, }; #[derive(Clone)] @@ -25,6 +26,7 @@ pub struct UITradeData { pub player_entity: Option<Entity>, pub shop_items: Vec<ItemTradeData>, pub hunger_descriptor: String, + pub is_starved: bool, } pub fn sync_ui_trade_data( @@ -43,6 +45,7 @@ pub fn sync_ui_trade_data( &TradingState, &HungerState, &Handle<TradeManifest>, + Option<&StarvationMarker>, )>, manifests: Res<Assets<TradeManifest>>, mut trade_data: ResMut<UITradeData>, @@ -64,21 +67,23 @@ pub fn sync_ui_trade_data( CurrentResidence::TravellingFrom(..) => String::new(), }); - let (town_entity, _, town_trading_state, hunger_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; - } - }; + let (town_entity, _, town_trading_state, hunger_state, manifest_handle, starvation) = + 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; trade_data.hunger_descriptor = hunger_state.get_town_descriptor(); + trade_data.is_starved = starvation.is_some(); for (item_name, trade_good) in TRADE_GOODS.iter() { let manifest = match manifests.get(manifest_handle) { diff --git a/game_core/src/ui/widgets/settings_panel.rs b/game_core/src/ui/widgets/settings_panel.rs index eafbf71377eb5894c74ae821d2bde5fb61977400..51a0c36f65cba7ae11697278e923cdcea9c7e5f4 100644 --- a/game_core/src/ui/widgets/settings_panel.rs +++ b/game_core/src/ui/widgets/settings_panel.rs @@ -121,6 +121,9 @@ pub fn render_settings_panel( AudioSettings, >| { settings.master_volume = (settings.master_volume - 0.05).max(0.0); + save_config_file(Options { + audio: *settings + }); })} /> <BackgroundBundle @@ -151,6 +154,9 @@ pub fn render_settings_panel( AudioSettings, >| { settings.master_volume = (settings.master_volume + 0.05).min(1.0); + save_config_file(Options { + audio: *settings + }); })} /> </BackgroundBundle> @@ -236,6 +242,9 @@ pub fn render_settings_panel( AudioSettings, >| { settings.ui_volume = (settings.ui_volume - 0.05).max(0.0); + save_config_file(Options { + audio: *settings + }); })} /> <BackgroundBundle @@ -266,6 +275,9 @@ pub fn render_settings_panel( AudioSettings, >| { settings.ui_volume = (settings.ui_volume + 0.05).min(1.0); + save_config_file(Options { + audio: *settings + }); })} /> </BackgroundBundle> diff --git a/game_core/src/ui/widgets/shop_panel.rs b/game_core/src/ui/widgets/shop_panel.rs index bca69bafa561b203e2f47dd5b21ad7be2cdd69c2..074d0868a7dc9bb292470ff2c2359029a83a3da5 100644 --- a/game_core/src/ui/widgets/shop_panel.rs +++ b/game_core/src/ui/widgets/shop_panel.rs @@ -76,7 +76,28 @@ pub fn render_shop_panel( ) -> bool { let parent_id = Some(entity); - if let Ok(props) = query.get(entity) { + if ui_data.is_starved { + rsx! { + <ElementBundle> + <TextWidgetBundle + text={TextProps { + content: "As you wander into the merchant's store, you find only ".to_string(), + size: 32.0, + ..Default::default() + }} + styles={KStyle { + color: value(Color::BLACK), + padding: edge_px(20.0), + top: stretch(1.0), + bottom: stretch(1.0), + left: stretch(1.0), + right: stretch(1.0), + ..Default::default() + }} + /> + </ElementBundle> + }; + } else if let Ok(props) = query.get(entity) { rsx! { <ElementBundle> <TextWidgetBundle diff --git a/game_core/src/ui/widgets/tavern_panel.rs b/game_core/src/ui/widgets/tavern_panel.rs index dbd1f45865757314538afe30ee250226e994bee5..078e114f022149e717015260c850f9dd8e22f909 100644 --- a/game_core/src/ui/widgets/tavern_panel.rs +++ b/game_core/src/ui/widgets/tavern_panel.rs @@ -12,21 +12,68 @@ use crate::ui::components::*; use crate::ui::prelude::*; use crate::ui::sync::{UITradeData, UITravelInfo}; use crate::ui::widgets::*; -use crate::world::{CurrentResidence, MapQuery, TownPaths}; +use crate::world::{ + CraftSpecialism, CurrentResidence, MapQuery, TownName, TownPaths, TradingState, +}; use crate::{basic_widget, empty_props, on_button_click}; empty_props!(TavernPanelProps); basic_widget!(TavernPanelProps => TavernPanel); +#[derive(Eq, Default, PartialEq, Debug, Clone, Component)] +pub struct TavernPanelState { + pub hint: Option<String>, +} + pub fn render_tavern_panel( In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, mut commands: Commands, ui_data: Res<UITradeData>, query: Query<&TavernPanelProps>, + state_query: Query<&TavernPanelState>, + foo: Query<(&TownName, &CraftSpecialism)>, ) -> bool { let parent_id = Some(entity); + let state_entity = widget_context.use_state(&mut commands, entity, TavernPanelState::default()); + + let buy_hint = on_button_click!( + ParamSet<( + Query<&mut TradingState, With<Player>>, + Query<(&TownName, &CraftSpecialism)>, + Query<&mut TavernPanelState> + )>, + |mut params: ParamSet<( + Query<&mut TradingState, With<Player>>, + Query<(&TownName, &CraftSpecialism)>, + Query<&mut TavernPanelState> + )>| { + let success = if let Ok(mut trade_state) = params.p0().get_single_mut() { + trade_state.spend_gold(50) + } else { + false + }; - if let Ok(props) = query.get(entity) { + let rumour = if success { + let query = params.p1(); + let mut viable_iter = query.iter(); + let viable_count = viable_iter.len(); + if viable_count < 1 { + None + } else { + let selected = viable_iter.nth(fastrand::usize(0..viable_count)); + selected.map(|(name, craft)| craft.format_rumour(&name.0)) + } + } else { + None + }; + + if let Ok(mut state_state) = params.p2().get_mut(state_entity) { + state_state.hint = rumour.clone(); + } + } + ); + + if let (Ok(props), Ok(state)) = (query.get(entity), state_query.get(state_entity)) { rsx! { <ElementBundle> <TextWidgetBundle @@ -38,12 +85,35 @@ pub fn render_tavern_panel( }} text={TextProps { size: 32.0, - content: String::from("A strange bartender eyes you suspiciously"), + content: if ui_data.is_starved { String::from("The corpse of a bartender slumps over the bar") } else { String::from("A strange bartender eyes you suspiciously") }, ..Default::default() }} /> + { if let Some(hint) = &state.hint { + constructor! { + <TextWidgetBundle + styles={KStyle { + top: px(20.0), + bottom: px(20.0), + left: stretch(1.0), + right: stretch(1.0), + color: value(Color::BLACK), + ..Default::default() + }} + text={TextProps { + size: 28.0, + content: format!("\"{}\"", &hint), + ..Default::default() + }} + /> + } + }} + + <ScrollContextProviderBundle> <ScrollBoxBundle> + { if state.hint.is_none() { + constructor! { <TextWidgetBundle styles={KStyle { top: px(20.0), @@ -59,8 +129,30 @@ pub fn render_tavern_panel( ..Default::default() }} /> + } + }} </ScrollBoxBundle> </ScrollContextProviderBundle> + + {if !ui_data.is_starved { + constructor! { + <ButtonWidget + props={ButtonWidgetProps { + font_size: 24.0, + left_icon: IconContent::Atlas(String::from("icons"), 8), + text: String::from("50g: Tip Barkeep"), + is_disabled: ui_data.player_gold < 50, + ..Default::default() + }} + styles={KStyle { + left: stretch(1.0), + right: stretch(1.0), + ..Default::default() + }} + on_event={buy_hint} + /> + } + }} </ElementBundle> }; } diff --git a/game_core/src/world/encounters.rs b/game_core/src/world/encounters.rs index 92ddbf9e154f40411833e76993adc7cee1e28242..66ac733d46d6353e5fba62cbe28ec76d2e46d1ab 100644 --- a/game_core/src/world/encounters.rs +++ b/game_core/src/world/encounters.rs @@ -28,8 +28,10 @@ impl EncounterType { title: String::from("Your journey begins"), description: String::from(include_str!("../assets/static/intro_text.txt")), options: vec![EncounterOption { + requirements: Vec::new(), label: String::from("Journey Forth"), - outcome: vec![], + outcomes: vec![], + hide_missing_reqs: true, }], }]), _ => None, @@ -132,12 +134,19 @@ impl From<Vec<EncounterZone>> for WorldZones { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "type")] pub enum EncounterOutcome { + Continue, + Description { + description: String, + }, GainResource { + description: String, resource_type: String, amount: (usize, usize), }, LoseResource { + description: String, resource_type: String, amount: (usize, usize), }, @@ -150,10 +159,35 @@ pub enum EncounterOutcome { }, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ChanceOutcome { + pub chance: usize, + pub outcome: EncounterOutcome, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum EncounterRequirement { + MinimumItemAmount { item: String, amount: usize }, + MaximumItemAmount { item: String, amount: usize }, + MinimumGoldAmount { item: String, amount: usize }, + MaximumGoldAmount { item: String, amount: usize }, + AnyFoodLessThan { amount: usize, or_equal: bool }, + AnyFoodGreaterThan { amount: usize, or_equal: bool }, +} + +fn default_false() -> bool { + false +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct EncounterOption { pub label: String, - pub outcome: Vec<EncounterOutcome>, + pub outcomes: Vec<EncounterOutcome>, + #[serde(default = "Vec::new")] + pub requirements: Vec<EncounterRequirement>, + #[serde(default = "default_false")] + pub hide_missing_reqs: bool, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -170,7 +204,8 @@ pub fn gen_encounter() -> Encounter { options: vec![ EncounterOption { label: BsVerb().fake(), - outcome: vec![EncounterOutcome::GainResource { + outcomes: vec![EncounterOutcome::GainResource { + description: Paragraph(3..5).fake(), resource_type: format!( "{} {}", BsAdj().fake::<String>(), @@ -182,10 +217,13 @@ pub fn gen_encounter() -> Encounter { (b, b + inc) }, }], + requirements: Vec::new(), + hide_missing_reqs: true, }, EncounterOption { label: BsVerb().fake(), - outcome: vec![EncounterOutcome::LoseResource { + outcomes: vec![EncounterOutcome::LoseResource { + description: Paragraph(3..5).fake(), resource_type: format!( "{} {}", BsAdj().fake::<String>(), @@ -197,6 +235,8 @@ pub fn gen_encounter() -> Encounter { (b, b + inc) }, }], + requirements: Vec::new(), + hide_missing_reqs: true, }, ], } diff --git a/game_core/src/world/hunger.rs b/game_core/src/world/hunger.rs index 67fd48b6a58a368ef61caa3bc7cdcba797ccbc12..ea6ca40d117374e64139f2fbb8394a935597d726 100644 --- a/game_core/src/world/hunger.rs +++ b/game_core/src/world/hunger.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; -use bevy::prelude::{Component, Entity, EventReader, Mut, Query, With, Without}; +use bevy::prelude::{Commands, Component, Entity, EventReader, Mut, Query, Res, With, Without}; use serde::{Deserialize, Serialize}; +use crate::assets::AssetHandles; use crate::const_data::get_goods_from_name_checked; use crate::states::Player; +use crate::world::spawning::apply_skull_marker; use crate::world::travel::WorldTickEvent; use crate::world::{TownName, TradeGood, TradingState}; @@ -46,6 +48,7 @@ impl HungerState { } } +#[derive(Component)] pub struct StarvationMarker; pub const PLAYER_FOOD_TARGET: usize = 25; @@ -144,9 +147,20 @@ fn process_hunger_state<'a>( } } +#[derive(Default)] +pub struct PlayerStarvedEvent; + pub fn handle_entity_starvation( - food_query: Query<(Entity, &HungerState)>, + mut commands: Commands, + assets: Res<AssetHandles>, + food_query: Query<(Entity, &HungerState), Without<StarvationMarker>>, player_query: Query<(), With<Player>>, town_query: Query<&TownName>, ) { + for (entity, hunger) in &food_query { + if hunger.sustenance == 0 { + apply_skull_marker(&mut commands, &assets, entity); + commands.entity(entity).insert(StarvationMarker); + } + } } diff --git a/game_core/src/world/mod.rs b/game_core/src/world/mod.rs index 7d2d877ebd7218c1b61825029a8ca1b991b53e28..23131befa50b65ee9efb40bfeda2e3b8cf6b2da4 100644 --- a/game_core/src/world/mod.rs +++ b/game_core/src/world/mod.rs @@ -90,6 +90,7 @@ impl Plugin for WorldPlugin { ConditionSet::new() .run_in_state(AppState::InGame) .with_system(encounters::notify_new_zone) + .with_system(hunger::handle_entity_starvation) .with_system(|mut events: ResMut<Events<WorldTickEvent>>| { events.clear(); }) diff --git a/game_core/src/world/spawning.rs b/game_core/src/world/spawning.rs index 016b38ba63c7382df467fe4c729342cf6d4b06e5..1625ab15de4635a3e2694e79a3eda5654608ba29 100644 --- a/game_core/src/world/spawning.rs +++ b/game_core/src/world/spawning.rs @@ -293,6 +293,9 @@ pub fn populate_world( } let world_zones = WorldZones::from_entities(level.px_hei, MapQuery::get_entities_of(level)); + let mut town_entities_a = Vec::new(); + let mut town_entities_b = Vec::new(); + MapQuery::get_entities_of(level) .into_iter() .filter(|instance| instance.identifier == *"Town") @@ -331,39 +334,70 @@ pub fn populate_world( .as_ref() .and_then(|pl| pl.0.town_states.get(&name)) { - Some((trade, hunger, tick)) => TownBundle { - town_name: TownName(name.clone()), - manifest: handle, - trade_state: trade.clone(), - hunger_state: hunger.clone(), - manifest_state: tick.clone(), - world_linked: WorldLinked, - location: TransformBundle::from_transform(Transform::from_translation( - transform, - )), - }, - None => TownBundle { - town_name: TownName(name.clone()), - manifest: handle, - trade_state: TradingState { - gold: fastrand::isize(0..250) + 250, - items: asset.create_randomised_inventory(), + Some((trade, hunger, tick, maybe_spec)) => ( + maybe_spec.clone(), + TownBundle { + town_name: TownName(name.clone()), + manifest: handle, + trade_state: trade.clone(), + hunger_state: hunger.clone(), + manifest_state: tick.clone(), + world_linked: WorldLinked, + location: TransformBundle::from_transform(Transform::from_translation( + transform, + )), }, - hunger_state: HungerState::initial_town(), - manifest_state: TradeManifestTickState::default(), - world_linked: WorldLinked, - location: TransformBundle::from_transform(Transform::from_translation( - transform, - )), - }, + ), + None => ( + None, + TownBundle { + town_name: TownName(name.clone()), + manifest: handle, + trade_state: TradingState { + gold: fastrand::isize(0..250) + 250, + items: asset.create_randomised_inventory(), + }, + hunger_state: HungerState::initial_town(), + manifest_state: TradeManifestTickState::default(), + world_linked: WorldLinked, + location: TransformBundle::from_transform(Transform::from_translation( + transform, + )), + }, + ), } }) - .for_each(|bundle| { + .for_each(|(maybe_spec, bundle)| { + let is_royal_lampoon = &bundle.town_name.0 == &String::from("The Royal Lampoon"); let ent = commands.spawn((bundle, VisibilityBundle::default())).id(); - apply_skull_marker(&mut commands, &assets, ent); + if let Some(spec) = maybe_spec { + commands.entity(ent).insert(spec); + } else if is_royal_lampoon { + town_entities_a.push(ent); + } }); - log::info!("Spec {:#?}", *SPECIALISMS); + if pending_load.is_none() { + let mut specialisms = (*SPECIALISMS).clone(); + let mut handling_a = true; + for spec in specialisms { + if town_entities_a.is_empty() && !town_entities_b.is_empty() { + handling_a = false; + } else if town_entities_b.is_empty() && !town_entities_a.is_empty() { + handling_a = true; + } + + if handling_a { + let entity = town_entities_a.remove(fastrand::usize(0..town_entities_a.len())); + commands.entity(entity).insert(spec); + town_entities_b.push(entity); + } else { + let entity = town_entities_b.remove(fastrand::usize(0..town_entities_b.len())); + commands.entity(entity).insert(spec); + town_entities_a.push(entity); + } + } + } commands.insert_resource(trade_routes); commands.insert_resource(world_zones); diff --git a/game_core/src/world/trading.rs b/game_core/src/world/trading.rs index 21cff5fa104dc72ab8bf3642dca1ec6ca439c42a..643fab9c6850c83b0724d77ac6275d28978f1e6f 100644 --- a/game_core/src/world/trading.rs +++ b/game_core/src/world/trading.rs @@ -100,6 +100,9 @@ impl TradeManifest { } } + inventory + .entry(String::from("Bread")) + .or_insert_with(|| fastrand::usize(8..15)); inventory } } diff --git a/raw_assets/calculator.ods b/raw_assets/calculator.ods new file mode 100644 index 0000000000000000000000000000000000000000..ac7f2d3c4e2b6649eadb0e5e4e2a11d3035e7223 Binary files /dev/null and b/raw_assets/calculator.ods differ diff --git a/raw_assets/specialisms.toml b/raw_assets/specialisms.toml index 1b8c9023fb20e7b4ec446ca58c338cd428a07a47..a4a926b921b5cc6f00a970c648a91c74be4366a8 100644 --- a/raw_assets/specialisms.toml +++ b/raw_assets/specialisms.toml @@ -1,23 +1,29 @@ [[specialism]] [specialism.requirements] -Tomato = 3 -Cheese = 2 Dough = 2 +Cheese = 2 +Tomato = 3 [specialism.production] Pizza = 2 [[specialism]] [specialism.requirements] Coal = 4 -"Iron Ore" = 3 +"Iron Ore" = 5 +[specialism.production] +"Iron Ingot" = 5 + +[[specialism]] +[specialism.requirements] +"Rough Gemstone" = 5 [specialism.production] -"Iron Ingot" = 3 +"Polished Gemstone" = 5 [[specialism]] [specialism.requirements] Lumber = 10 [specialism.production] -"Spare Wheel" = 3 +"Spare Wheel" = 2 [[specialism]] [specialism.requirements] @@ -36,3 +42,9 @@ Dough = 3 Dough = 4 [specialism.production] Bread = 3 + +[[specialism]] +[specialism.requirements] +Grapes = 4 +[specialism.production] +Wine = 6 diff --git a/raw_assets/trade_goods.toml b/raw_assets/trade_goods.toml index a92a5d5485603c743570b002db62b7ec6cfde9fa..f7ad6a676fd43b6d5c9a6c94b382ca9124437fce 100644 --- a/raw_assets/trade_goods.toml +++ b/raw_assets/trade_goods.toml @@ -1,10 +1,10 @@ -["Armour"] +[Armour] name = "Armour" icon = ["icons", 12] food_value = 0 gold_value = 12 -["Weapons"] +[Weapons] name = "Weapons" icon = ["icons", 1] food_value = 0 @@ -16,23 +16,23 @@ icon = ["icons", 13] food_value = 0 gold_value = 27 -["Ale"] +[Ale] name = "Ale" icon = ["icons", 11] food_value = 0 gold_value = 6 -["Milk"] +[Milk] name = "Milk" icon = ["icons", 2] -food_value = 2 -gold_value = 5 +food_value = 3 +gold_value = 6 -["Cheese"] +[Cheese] name = "Cheese" icon = ["icons", 3] -food_value = 3 -gold_value = 6 +food_value = 4 +gold_value = 9 ["Spare Wheel"] name = "Spare Wheel" @@ -40,38 +40,113 @@ icon = ["icons", 28] food_value = 0 gold_value = 125 -["Berries"] +[Berries] name = "Berries" icon = ["icons", 27] -food_value = 1 +food_value = 2 gold_value = 4 -["Corn"] +[Corn] name = "Corn" icon = ["icons", 26] food_value = 2 -gold_value = 6 +gold_value = 4 -["Tomato"] +[Tomato] name = "Tomato" icon = ["icons", 25] -food_value = 2 +food_value = 3 gold_value = 6 -["Chili"] +[Chili] name = "Chili" icon = ["icons", 24] -food_value = 2 +food_value = 4 gold_value = 8 -["Grapes"] +[Grapes] name = "Grapes" icon = ["icons", 23] -food_value = 1 -gold_value = 9 +food_value = 4 +gold_value = 8 -["Wheat"] +[Wheat] name = "Wheat" icon = ["icons", 22] -food_value = 1 -gold_value = 3 +food_value = 2 +gold_value = 4 + +[Dough] +name = "Dough" +icon = ["icons", 38] +food_value = 0 +gold_value = 9 + +[Bread] +name = "Bread" +icon = ["icons", 35] +food_value = 9 +gold_value = 14 + +[Pizza] +name = "Pizza" +icon = ["icons", 36] +food_value = 18 +gold_value = 36 + +[Lumber] +name = "Lumber" +icon = ["icons", 29] +food_value = 0 +gold_value = 10 + +[Coal] +name = "Coal" +icon = ["icons", 30] +food_value = 0 +gold_value = 13 + +["Iron Ore"] +name = "Iron Ore" +icon = ["icons", 31] +food_value = 0 +gold_value = 8 + +[Iron] +name = "Iron" +icon = ["icons", 32] +food_value = 0 +gold_value = 33 + +["Rough Gemstone"] +name = "Rough Gemstone" +icon = ["icons", 33] +food_value = 0 +gold_value = 12 + +["Polished Gemstone"] +name = "Polished Gemstone" +icon = ["icons", 34] +food_value = 0 +gold_value = 19 + + +["Religious Artifact"] +name = "Religious Artifact" +icon = ["icons", 34] +food_value = 0 +gold_value = 125 + + +["Strange Doll"] +name = "Polished Gemstone" +icon = ["icons", 34] +food_value = 0 +gold_value = 19 + +["Wine"] +name = "Wine" +icon = ["icons", 37] +food_value = 7 +gold_value = 12 +