Skip to content
Snippets Groups Projects
specialism.rs 3.87 KiB
Newer Older
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);
		}
	}
}