use std::{fmt::Debug, marker::PhantomData};
pub use rustorio_derive::{TechnologyEx, technology_doc};
use crate::{
ResourceType, Sealed,
recipe::{Recipe, RecipeEx},
resources::{Bundle, Resource},
};
pub trait Technology: Sealed + Debug + Sized + TechnologyEx {
const NAME: &'static str;
const REQUIRED_RESEARCH_POINTS: u32 = Self::REQUIRED_RESEARCH_POINTS_EX;
type Unlocks;
fn research(
self,
research_points: Bundle<ResearchPoint<Self>, { Self::REQUIRED_RESEARCH_POINTS }>,
) -> Self::Unlocks;
}
#[doc(hidden)]
pub trait TechnologyEx {
type Inputs: Debug;
type InputAmountsType: Debug;
type InputBundle: Debug;
const INPUT_AMOUNTS: Self::InputAmountsType;
const POINT_RECIPE_TIME: u64;
const REQUIRED_RESEARCH_POINTS_EX: u32;
fn new_inputs() -> Self::Inputs;
fn iter_inputs(items: &mut Self::Inputs)
-> impl Iterator<Item = (&'static str, u32, &mut u32)>;
}
#[derive(Debug)]
#[non_exhaustive]
pub struct ResearchPoint<T: Technology> {
_marker: PhantomData<T>,
}
impl<T: Technology> Sealed for ResearchPoint<T> {}
impl<T: Technology> ResourceType for ResearchPoint<T> {
const NAME: &'static str = T::NAME;
}
#[derive(Debug)]
pub struct TechRecipe<T: Technology> {
_marker: PhantomData<T>,
}
impl<T> Recipe for TechRecipe<T>
where
T: Technology,
{
const TIME: u64 = T::POINT_RECIPE_TIME;
type Inputs = T::Inputs;
type InputAmountsType = T::InputAmountsType;
const INPUT_AMOUNTS: Self::InputAmountsType = T::INPUT_AMOUNTS;
type Outputs = (Resource<ResearchPoint<T>>,);
type OutputAmountsType = (u32,);
const OUTPUT_AMOUNTS: (u32,) = (1,);
fn new_inputs() -> Self::Inputs {
T::new_inputs()
}
fn new_outputs() -> Self::Outputs {
(Resource::new_empty(),)
}
}
impl<T: Technology> RecipeEx for TechRecipe<T> {
type InputBundle = T::InputBundle;
type OutputBundle = Bundle<ResearchPoint<T>, 1>;
fn new_output_bundle() -> Self::OutputBundle {
Bundle::<ResearchPoint<T>, 1>::new()
}
fn iter_inputs(
items: &mut Self::Inputs,
) -> impl Iterator<Item = (&'static str, u32, &mut u32)> {
T::iter_inputs(items)
}
fn iter_outputs(
items: &mut Self::Outputs,
) -> impl Iterator<Item = (&'static str, u32, &mut u32)> {
[(ResearchPoint::<T>::NAME, 1u32, &mut items.0.amount)].into_iter()
}
}
pub const fn tech_recipe<T: Technology>() -> TechRecipe<T> {
TechRecipe {
_marker: PhantomData,
}
}