Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion rustorio-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ impl ToTokens for Crate {
let found_crate =
proc_macro_crate::crate_name("rustorio-engine").expect("Failed to get crate name");
match found_crate {
FoundCrate::Itself => quote! {crate}.to_tokens(tokens),
// Emit the crate name for doctests.
FoundCrate::Itself => quote! {rustorio_engine}.to_tokens(tokens),
FoundCrate::Name(name) => {
let crate_ident = Ident::new(&name, Span::call_site());
quote! {::#crate_ident}.to_tokens(tokens);
Expand Down
80 changes: 80 additions & 0 deletions rustorio-engine/src/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ pub trait MultiBundle: Sized + std::fmt::Debug {
) -> impl Iterator<Item = (&'static str, u32, &'a mut u32)>;
}

/// Multiply the amounts in a bundle tuple by the given constant.
pub trait MultiBundleMultiply<const N: u32>: MultiBundle {
/// Bundle tuple identical to `Self` except with all amounts multiplied by `N`.
type Multiplied: MultiBundle;
}
/// Transform a tuple of bundles by multiplying all bundle quantities by `N`.
pub type MultiplyMultiBundle<MB, const N: u32> = <MB as MultiBundleMultiply<N>>::Multiplied;

// Special untupled case, for e.g. tech recipes that don't return a tuple.
impl<R1: ResourceType, const N1: u32> MultiBundle for Bundle<R1, N1> {
type AsResources = (Resource<R1>,);
Expand Down Expand Up @@ -75,6 +83,14 @@ impl<R1: ResourceType, const N1: u32> MultiBundle for Bundle<R1, N1> {
}
}

impl<const N: u32, R1: ResourceType, const N1: u32> MultiBundleMultiply<N> for Bundle<R1, N1>
where
// See https://github.com/rust-lang/rust/issues/145069 for why `Copy`
[(); { N1 * N } as usize]: Copy,
{
type Multiplied = Bundle<R1, { N1 * N }>;
}

macro_rules! replace_expr {
($_t:tt, $($sub:tt)*) => {
$($sub)*
Expand Down Expand Up @@ -162,6 +178,19 @@ macro_rules! impl_multi_bundle {
.into_iter()
}
}

impl<
const N: u32,
$($ty: ResourceType, const $amount: u32),*
> MultiBundleMultiply<N> for ($(Bundle<$ty, $amount>,)*)
where
// See https://github.com/rust-lang/rust/issues/145069 for why `Copy`
$(
[(); { $amount * N } as usize]: Copy,
)*
{
type Multiplied = ($(Bundle<$ty, { $amount * N }>,)*);
}
};
}

Expand Down Expand Up @@ -225,4 +254,55 @@ pub trait HandRecipe: std::fmt::Debug + Sealed + Recipe {
tick.advance_by(Self::TIME);
Self::OutputBundle::new_bundle(creation_token())
}

/// Crafts the recipe `N` times by consuming `N` input bundles and producing `N` output bundles.
/// Advances the provided `Tick` by `N` times the recipe's time.
///
/// Note: Call this function using an explicit `N`: `MyRecipe::craft_n::<42>(..)`; `N` can't be
/// inferred and omitting it may give rise to confusing errors.
fn craft_n<const N: u32>(
tick: &mut Tick,
inputs: MultiplyMultiBundle<Self::InputBundle, N>,
) -> MultiplyMultiBundle<Self::OutputBundle, N>
where
Self::InputBundle: MultiBundleMultiply<N>,
Self::OutputBundle: MultiBundleMultiply<N, Multiplied: MultiBundle>,
{
let token = creation_token();
let _ = inputs;
tick.advance_by(Self::TIME * N as u64);
<<Self::OutputBundle as MultiBundleMultiply<N>>::Multiplied as MultiBundle>::new_bundle(
token,
)
}
}

#[test]
fn test_handcrafting() {
use rustorio_derive::Recipe;

use crate as rustorio_engine; // For the derive macros

crate::resource_type!(Copper);
crate::resource_type!(CopperWire);

#[derive(Debug, Clone, Copy, Recipe)]
#[recipe_doc]
#[recipe_inputs(
(1, Copper),
)]
#[recipe_outputs(
(2, CopperWire),
)]
#[recipe_ticks(1)]
pub struct CopperWireRecipe;
impl Sealed for CopperWireRecipe {}
impl HandRecipe for CopperWireRecipe {}

let mut tick = Tick::start(10000);
let mut copper: Resource<Copper> = crate::resource(creation_token(), 42);
let _: (Bundle<CopperWire, 2>,) =
CopperWireRecipe::craft(&mut tick, (copper.bundle().unwrap(),));
let _: (Bundle<CopperWire, 10>,) =
CopperWireRecipe::craft_n::<5>(&mut tick, (copper.bundle().unwrap(),));
}
2 changes: 1 addition & 1 deletion rustorio-engine/src/tick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl TickSnapshot {
/// # Examples
///
/// Let's say we have two furnaces the we want to fill with `iron_ore` and `copper_ore` respectively, and then advance time so they can smelt the ore into ingots:
/// ```
/// ```ignore
/// // Add ore to the furnaces at the current tick
/// furnace1.inputs(&tick).0 += iron_ore;
/// furnace2.inputs(&tick).0 += copper_ore;
Expand Down