use std::fmt::format; use bevy::prelude::{Component, EventReader, Query}; use bevy::utils::HashMap; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use crate::world::travel::WorldTickEvent; use crate::world::TradingState; #[derive(Clone, Debug, Default, Component, Serialize, Deserialize)] pub struct CraftSpecialism { pub requirements: HashMap<String, usize>, pub production: HashMap<String, usize>, } lazy_static! { static ref REQUIREMENT_TEMPLATES: Vec<String> = { let mut items = Vec::with_capacity(4); items.push(String::from("I hear that the artisans at [[TOWN]] are pretty good at working with [[ITEMS]]. A wonder to behold!",)); items.push(String::from("I remember the last time I was at [[TOWN]]. I saw a craftsman making something with [[ITEMS]]. I hope I get to see that again one day...",)); items.push(String::from("If you happen to have [[ITEMS]], consider popping over to [[TOWN]]. I hear there's high demand there!",)); items.push(String::from("I've heard that there are a lot of folk able to make use of [[ITEMS]] over in [[TOWN]].")); items }; static ref PRODUCTION_TEMPLATES: Vec<String> = { let mut items = Vec::with_capacity(3); items.push(String::from("Have you ever seen the craftsfolk over in [[TOWN]] making [[ITEMS]]? It's a wonder to see. I heard they need a few bits and bobs to get going though.")); items.push(String::from("Got a hankering for [[ITEMS]]? If you can get some supplies to [[TOWN]], you might be in luck.")); items.push(String::from("My cousin over in [[TOWN]] is a dab hand at making [[ITEMS]]. Doesn't usually have the supplies for it, though.")); items }; } impl CraftSpecialism { pub fn requirements_satisfied(&self, inventory: &TradingState) -> bool { self.requirements.iter().all(|(name, amount)| { inventory .items .get(name) .map(|quantity| quantity >= amount) .unwrap_or(false) }) } pub fn apply_to_inventory(&self, inventory: &mut TradingState) -> bool { if self.requirements_satisfied(inventory) { for (name, amount) in self.requirements.iter() { inventory.remove_items(name, *amount); } for (name, amount) in self.production.iter() { inventory.add_items(name, *amount); } true } else { false } } fn format_item_map(map: &HashMap<String, usize>) -> String { match map.len() { 0 => "...oh, I forget".to_string(), 1 => format!("an {}", map.keys().next().unwrap()), 2 => format!( "{} and {}", map.keys().nth(0).unwrap(), map.keys().nth(1).unwrap() ), _ => { let first = fastrand::usize(0..map.len()); let mut second = fastrand::usize(0..map.len()); while second == first { second = fastrand::usize(0..map.len()); } format!( "{}, {}, and...some other things, I think", map.keys().nth(first).unwrap(), map.keys().nth(second).unwrap() ) } } } pub fn format_requirements(&self) -> String { Self::format_item_map(&self.requirements) } pub fn format_production(&self) -> String { Self::format_item_map(&self.production) } pub fn format_rumour(&self, town_name: impl ToString) -> String { let is_production = fastrand::bool(); if is_production { let stub = &PRODUCTION_TEMPLATES[fastrand::usize(0..PRODUCTION_TEMPLATES.len())]; let output = stub .replace("[[TOWN]]", town_name.to_string().as_str()) .replace("[[ITEMS]]", self.format_production().as_str()); output } else { let stub = &REQUIREMENT_TEMPLATES[fastrand::usize(0..REQUIREMENT_TEMPLATES.len())]; let output = stub .replace("[[TOWN]]", town_name.to_string().as_str()) .replace("[[ITEMS]]", self.format_requirements().as_str()); output } } } pub fn check_specialisms( mut query: Query<(&mut TradingState, &CraftSpecialism)>, events: EventReader<WorldTickEvent>, ) { if !events.is_empty() { for (mut trading_state, specialism) in &mut query { specialism.apply_to_inventory(&mut trading_state); } } }