233 lines
7.3 KiB
C#
233 lines
7.3 KiB
C#
namespace FoodFactory.Recipes;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Arch.Core;
|
|
using Arch.Core.Extensions;
|
|
using Arch.Core.Extensions.Dangerous;
|
|
using FoodFactory.Items;
|
|
using FoodFactory.Math;
|
|
using Godot;
|
|
|
|
public abstract record Recipe(string Name, BlueprintId[]? BlueprintIds, bool[] RequireAll, Tags[] Inputs, Tags[] Exclude, RecipeOutput RecipeOutput, RecipeAction Action = default, bool InputsOrdered = true)
|
|
{
|
|
public abstract bool CanProcess(in RecipeContext context);
|
|
public abstract RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder);
|
|
}
|
|
public readonly record struct RecipeOutput
|
|
{
|
|
public int? Min { get; }
|
|
public int? Max { get; }
|
|
[MemberNotNullWhen(true, nameof(Min), nameof(Max))]
|
|
public bool IsExact => Min.HasValue && Max.HasValue && Min == Max;
|
|
[MemberNotNullWhen(true, nameof(Min), nameof(Max))]
|
|
public bool IsRange => Min.HasValue && Max.HasValue && Min != Max;
|
|
public bool IsUnbounded => Min == null && Max == null;
|
|
|
|
public RecipeOutput(int exact)
|
|
{
|
|
Min = exact;
|
|
Max = exact;
|
|
}
|
|
|
|
public RecipeOutput(int min, int max)
|
|
{
|
|
Min = min;
|
|
Max = max;
|
|
}
|
|
private RecipeOutput(int? min, int? max)
|
|
{
|
|
Min = min;
|
|
Max = max;
|
|
}
|
|
|
|
public static RecipeOutput Any => new(null, null);
|
|
}
|
|
public struct CompiledRecipe
|
|
{
|
|
|
|
public Recipe RecipeRef;
|
|
// public BlueprintId[] InputBlueprints;
|
|
public ItemEntry[] ItemEntries;
|
|
public bool IsOrdered;
|
|
}
|
|
public struct ItemEntry
|
|
{
|
|
public bool RequireAll;
|
|
public Tags InputTags;
|
|
public Tags ExcludeTags;
|
|
public BlueprintId? Blueprint;
|
|
}
|
|
public static class RecipeCompiler
|
|
{
|
|
public static CompiledRecipe Compile(Recipe recipe) => new()
|
|
{
|
|
RecipeRef = recipe,
|
|
ItemEntries = BuildEntries(recipe),
|
|
// IsOrdered = recipe.InputsOrdered,
|
|
};
|
|
private static ItemEntry[] BuildEntries(Recipe recipe)
|
|
{
|
|
var entries = new ItemEntry[recipe.Inputs.Length];
|
|
|
|
for (var i = 0; i < entries.Length; i++)
|
|
{
|
|
entries[i] = new ItemEntry
|
|
{
|
|
RequireAll = recipe.RequireAll[i],
|
|
InputTags = recipe.Inputs[i],
|
|
ExcludeTags = recipe.Exclude[i],
|
|
// Blueprint = recipe.BlueprintIds?[i],
|
|
};
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
}
|
|
public record BurnItemRecipe : Recipe
|
|
{
|
|
public BurnItemRecipe() : base("Burn_item",
|
|
BlueprintIds: null,
|
|
RequireAll: [false],
|
|
Inputs: [new Tags("burnable")],
|
|
Exclude: [new Tags("burned")],
|
|
RecipeOutput: RecipeOutput.Any,
|
|
Action: new RecipeAction("cook"),
|
|
InputsOrdered: true)
|
|
{
|
|
|
|
}
|
|
|
|
public override bool CanProcess(in RecipeContext context)
|
|
{
|
|
if (context.Items[0].TryGet<Temperature>(out var temp) && context.Items[0].TryGet<BurnableTemp>(out var burnableTemp) && temp > burnableTemp.Temperature)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder) => new(BurnItem);
|
|
|
|
private void BurnItem(RecipeContext context)
|
|
{
|
|
// context.Entity[0].Remove<BurnableTemp>();
|
|
context.Items[0].Add(new BurnedItem(context.Items[0].Get<Temperature>(), "Was Burned as some time thing"));
|
|
context.Items[0].Set(context.Items[0].Get<Tags>().WithOut("burnable").With("burned"));
|
|
context.Items[0].Set($"Burned_{context.Items[0].Get<Name>().Value}");
|
|
if (context.Items[0].Has<Color>())
|
|
{
|
|
context.Items[0].Set(context.Items[0].Get<Color>() * Colors.Black);
|
|
}
|
|
else
|
|
{
|
|
context.Items[0].Add(Colors.Black);
|
|
}
|
|
}
|
|
}
|
|
public record OnionSliceRecipe() : Recipe(Name: "Onion_Slice",
|
|
BlueprintIds: null,
|
|
RequireAll: [false],
|
|
Inputs: [new Tags("onion", "sliceable")],
|
|
Exclude: [new Tags()],
|
|
Action: "slice",
|
|
RecipeOutput: new RecipeOutput(2)
|
|
)
|
|
{
|
|
public override bool CanProcess(in RecipeContext context) => true;
|
|
|
|
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder)
|
|
{
|
|
builder.AddRemove(true);
|
|
builder.AddCreate(Slice);
|
|
builder.AddCreate(Slice);
|
|
return builder.Build();
|
|
}
|
|
private Entity Slice(in RecipeContext context)
|
|
{
|
|
Span<Entity> span = stackalloc Entity[1];
|
|
var signature = context.World.GetSignature(context.Items[0]);
|
|
context.World.Create(span, signature, 1);
|
|
PotatoCookRecipe.CopyEntities(context.Items, span, context.World);
|
|
span[0].Set(new Name("Sliced " + context.Items[0].Get<Name>().Value));
|
|
span[0].Set(new Mass(context.Items[0].Get<Mass>().Value * .5));
|
|
span[0].Set(span[0].Get<Tags>().With("sliced"));
|
|
return span[0];
|
|
}
|
|
}
|
|
public record PotatoCookRecipe() : Recipe(Name: "Potato_Cook",
|
|
BlueprintIds: null,
|
|
RequireAll: [false],
|
|
Inputs: [new Tags("potato", "raw")],
|
|
Exclude: [new Tags()],
|
|
InputsOrdered: true,
|
|
Action: "cook",
|
|
RecipeOutput: new RecipeOutput(1))
|
|
{
|
|
public override bool CanProcess(in RecipeContext context)
|
|
{
|
|
if (!(context.Items[0].TryGet<Temperature>(out var temp) && temp.Fahrenheit > 350))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override RecipeResult Process(in RecipeContext context, ref RecipeResultBuilder builder)
|
|
{
|
|
builder.AddRemove(true);
|
|
builder.AddCreate(NewPotato);
|
|
return builder.Build();
|
|
}
|
|
private Entity NewPotato(in RecipeContext context)
|
|
{
|
|
Span<Entity> span = stackalloc Entity[1];
|
|
var signature = context.World.GetSignature(context.Items[0]);
|
|
context.World.Create(span, signature, 1);
|
|
CopyEntity(context.Items[0], span[0], context.World);
|
|
|
|
span[0].Set(new Name("Cooked_Potato"));
|
|
span[0].Set(context.Items[0].Get<Tags>().With("cooked").WithOut("raw"));
|
|
// span[0].Set(context.Entity[0].Get<Temperature>());
|
|
if (context.Items[0].Has<Color>())
|
|
{
|
|
context.Items[0].Set(context.Items[0].Get<Color>() * Colors.Brown);
|
|
}
|
|
else
|
|
{
|
|
context.Items[0].Add(Colors.Brown);
|
|
}
|
|
return span[0];
|
|
}
|
|
public static void CopyEntities(Span<Entity> from, Span<Entity> to, World world)
|
|
{
|
|
Debug.Assert(from.Length == to.Length);
|
|
for (int i = 0; i < from.Length; i++)
|
|
{
|
|
CopyEntity(from[i], to[i], world);
|
|
}
|
|
|
|
}
|
|
public static void CopyEntity(Entity src, Entity dst, World world)
|
|
{
|
|
var srcData = world.GetEntityData(src);
|
|
var dstData = world.GetEntityData(dst);
|
|
|
|
var components = srcData.Archetype
|
|
.GetChunk(srcData.Slot.ChunkIndex)
|
|
.Components;
|
|
var componentsDst = dstData.Archetype
|
|
.GetChunk(dstData.Slot.ChunkIndex)
|
|
.Components;
|
|
|
|
int srcIndex = srcData.Slot.Index;
|
|
int dstIndex = dstData.Slot.Index;
|
|
|
|
for (int i = 0; i < components.Length; i++)
|
|
{
|
|
componentsDst[i].SetValue(components[i].GetValue(srcIndex), dstIndex);
|
|
}
|
|
}
|
|
}
|