Skip to main content

rustorio_engine/
research.rs

1//! Technologies can be unlocked by consuming science packs.
2//! They usually unlock new recipes or further technologies.
3//!
4//! This module defines the the science pack resources and the `Technology` trait.
5
6use std::{fmt::Debug, marker::PhantomData};
7
8pub use rustorio_derive::{TechnologyEx, technology_doc};
9
10use crate::{
11    ResourceType, Sealed,
12    recipe::{Recipe, RecipeEx},
13    resources::{Bundle, Resource},
14};
15
16/// A technology can be unlocked out by calling the `research` method with the required science packs.
17/// This will consume the science packs and the technology itself, and return whatever the technology unlocks, mostly recipes and other technologies.
18pub trait Technology: Sealed + Debug + Sized + TechnologyEx {
19    /// The name of the technology.
20    const NAME: &'static str;
21    /// How many of this technology's research points (`ResearchPoint<T>`) are needed to complete the research.
22    const REQUIRED_RESEARCH_POINTS: u32 = Self::REQUIRED_RESEARCH_POINTS_EX;
23
24    /// The reward for completing this technology.
25    type Unlocks;
26
27    /// Carries out the research by consuming the required science packs and the research itself, returning whatever this research unlocks.
28    fn research(
29        self,
30        research_points: Bundle<ResearchPoint<Self>, { Self::REQUIRED_RESEARCH_POINTS }>,
31    ) -> Self::Unlocks;
32}
33
34/// A trait handling the implementation details for a technology. Should only be implemented via the `#[derive(TechnologyEx)]` macro.
35#[doc(hidden)]
36pub trait TechnologyEx {
37    /// The inputs needed to create one research point for this technology.
38    /// Typically a tuple of multiple `RecipeItem`s.
39    type Inputs: Debug;
40    /// The type for `Self::InputAmountsType`, which is used to allow users to
41    /// access the input amount for each of the input resource types, per recipe cycle.
42    type InputAmountsType: Debug;
43    /// A type guaranteed to contain exactly the input resources for one research point.
44    /// Used in hand crafting.
45    type InputBundle: Debug;
46    /// Amount for each of the input resource types, per recipe cycle.
47    const INPUT_AMOUNTS: Self::InputAmountsType;
48    /// The amount of ticks it takes to create one research point for this technology.
49    const POINT_RECIPE_TIME: u64;
50    /// How many of this technology's research points (`ResearchPoint<T>`) are needed to complete the research.
51    const REQUIRED_RESEARCH_POINTS_EX: u32;
52
53    /// Factory function to create a new `Self::Inputs` with zero resources.
54    fn new_inputs() -> Self::Inputs;
55
56    /// Iterator helper over `Self::Inputs`.
57    fn iter_inputs(items: &mut Self::Inputs)
58    -> impl Iterator<Item = (&'static str, u32, &mut u32)>;
59}
60
61/// A resource type representing one research point for a specific `Technology`.
62/// Use them in the `research` method of the corresponding `Technology` to unlock the technology.
63#[derive(Debug)]
64#[non_exhaustive]
65pub struct ResearchPoint<T: Technology> {
66    _marker: PhantomData<T>,
67}
68
69impl<T: Technology> Sealed for ResearchPoint<T> {}
70impl<T: Technology> ResourceType for ResearchPoint<T> {
71    const NAME: &'static str = T::NAME;
72}
73
74/// A recipe for producing research points for specific technologies.
75#[derive(Debug)]
76pub struct TechRecipe<T: Technology> {
77    _marker: PhantomData<T>,
78}
79
80impl<T> Recipe for TechRecipe<T>
81where
82    T: Technology,
83{
84    const TIME: u64 = T::POINT_RECIPE_TIME;
85    type Inputs = T::Inputs;
86    type InputAmountsType = T::InputAmountsType;
87    const INPUT_AMOUNTS: Self::InputAmountsType = T::INPUT_AMOUNTS;
88    type Outputs = (Resource<ResearchPoint<T>>,);
89
90    type OutputAmountsType = (u32,);
91
92    const OUTPUT_AMOUNTS: (u32,) = (1,);
93
94    fn new_inputs() -> Self::Inputs {
95        T::new_inputs()
96    }
97
98    fn new_outputs() -> Self::Outputs {
99        (Resource::new_empty(),)
100    }
101}
102
103impl<T: Technology> RecipeEx for TechRecipe<T> {
104    type InputBundle = T::InputBundle;
105    type OutputBundle = Bundle<ResearchPoint<T>, 1>;
106
107    fn new_output_bundle() -> Self::OutputBundle {
108        Bundle::<ResearchPoint<T>, 1>::new()
109    }
110
111    fn iter_inputs(
112        items: &mut Self::Inputs,
113    ) -> impl Iterator<Item = (&'static str, u32, &mut u32)> {
114        T::iter_inputs(items)
115    }
116
117    fn iter_outputs(
118        items: &mut Self::Outputs,
119    ) -> impl Iterator<Item = (&'static str, u32, &mut u32)> {
120        [(ResearchPoint::<T>::NAME, 1u32, &mut items.0.amount)].into_iter()
121    }
122}
123
124/// Creates a new `TechRecipe<T>` for use in a `Machine`.
125/// Should not be reexported, as that would allow players to create research points for researches they have not unlocked yet.
126pub const fn tech_recipe<T: Technology>() -> TechRecipe<T> {
127    TechRecipe {
128        _marker: PhantomData,
129    }
130}