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
32 changes: 32 additions & 0 deletions S1API/Internal/Patches/ChemistryStationPatches.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
#if (IL2CPPMELON)
using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
using S1ObjectScripts = Il2CppScheduleOne.ObjectScripts;
using S1StationFramework = Il2CppScheduleOne.StationFramework;
using S1UIStations = Il2CppScheduleOne.UI.Stations;
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
using S1ItemFramework = ScheduleOne.ItemFramework;
using S1ObjectScripts = ScheduleOne.ObjectScripts;
using S1StationFramework = ScheduleOne.StationFramework;
using S1UIStations = ScheduleOne.UI.Stations;
#endif

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using HarmonyLib;
using S1API.Internal.Utils;
using S1API.Items;
using S1API.Logging;
using S1API.Stations;
using UnityEngine;
Expand Down Expand Up @@ -269,5 +273,33 @@ private static void EnsureRecipeEntries(S1UIStations.ChemistryStationCanvas canv

return null;
}

[HarmonyPatch(typeof(S1StationFramework.StationRecipe), "CalculateQuality")]
[HarmonyPrefix]
private static bool UseCustomCalcMethods(S1StationFramework.StationRecipe __instance,
ref S1ItemFramework.EQuality __result)
{
// Exit early out of patch if instance or recipeID is null
if (__instance == null) return true;
string? instanceRecipeId;
try { instanceRecipeId = __instance.RecipeID; } catch { instanceRecipeId = null; }
if (string.IsNullOrWhiteSpace(instanceRecipeId)) return true;
var currentAddedRecipe =
ChemistryStationRecipes.GetAll().FirstOrDefault(r => instanceRecipeId == __instance.RecipeID);
// If this recipe is not one of ours, exit early from patch
if (currentAddedRecipe == null) return true;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Use default quality calculation for non-absolute methods
if (currentAddedRecipe.QualityCalculationMethod != QualityCalculationMethod.Absolute) return true;
var product = currentAddedRecipe.Product.ItemId;
var itemDefinition = ItemManager.GetItemDefinition(product);
if (itemDefinition is not QualityItemDefinition qualityItemDefinition)
{
Logger.Warning($"[S1API] Absolute quality calculation method specified for recipe '{currentAddedRecipe.RecipeID}' but product '{product}' is not a quality item. Falling back to default calculation.");
return true;
}

__result = (S1ItemFramework.EQuality)qualityItemDefinition.DefaultQuality;
return false;
}
}
}
50 changes: 49 additions & 1 deletion S1API/Stations/ChemistryStationRecipe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@ internal ChemistryStationRecipe(
string recipeId,
string title,
int cookTimeMinutes,
ChemistryStationRecipeTemperature temperature,
Color finalLiquidColor,
ChemistryStationRecipeProduct product,
IReadOnlyList<ChemistryStationRecipeIngredient> ingredients)
IReadOnlyList<ChemistryStationRecipeIngredient> ingredients,
QualityCalculationMethod qualityCalculationMethod)
{
S1StationRecipe = stationRecipe;
RecipeID = recipeId;
Title = title;
CookTimeMinutes = cookTimeMinutes;
Temperature = temperature;
FinalLiquidColor = finalLiquidColor;
Product = product;
Ingredients = ingredients;
QualityCalculationMethod = qualityCalculationMethod;
}

/// <summary>
Expand All @@ -50,6 +54,11 @@ internal ChemistryStationRecipe(
/// Cook time in minutes.
/// </summary>
public int CookTimeMinutes { get; }

/// <summary>
/// Temperatures required by the recipe.
/// </summary>
public ChemistryStationRecipeTemperature Temperature { get; }

/// <summary>
/// UI liquid color for the final product.
Expand All @@ -66,6 +75,11 @@ internal ChemistryStationRecipe(
/// Each group can have multiple acceptable item IDs (variants).
/// </summary>
public IReadOnlyList<ChemistryStationRecipeIngredient> Ingredients { get; }

/// <summary>
/// Calculation method used when determining resulting product's quality.
/// </summary>
public QualityCalculationMethod QualityCalculationMethod { get; }

/// <summary>
/// Returns the native product item definition.
Expand Down Expand Up @@ -116,4 +130,38 @@ internal ChemistryStationRecipeIngredient(IReadOnlyList<string> itemIds, int qua
/// </summary>
public int Quantity { get; }
}

/// <summary>
/// Temperature ranges for the recipe, used mainly in the cooking minigame.
/// </summary>
public sealed class ChemistryStationRecipeTemperature
{
internal ChemistryStationRecipeTemperature(float cookTemperature, float tolerance)
{
CookTemperature = cookTemperature;
CookTemperatureTolerance = tolerance;
}

/// <summary>
/// Target cook temperature.
/// </summary>
public float CookTemperature { get; }

/// <summary>
/// Tolerance for cook temperature.
/// </summary>
public float CookTemperatureTolerance { get; }

/// <summary>
/// Lower bound of the acceptable cook temperature range (inclusive).
/// Used in the minigame - lower values will not start the recipe.
/// </summary>
public float CookTemperatureLowerBound => CookTemperature - CookTemperatureTolerance;

/// <summary>
/// Upper bound of the acceptable cook temperature range (inclusive).
/// Used in the minigame - higher values will cause the recipe to fail.
/// </summary>
public float CookTemperatureUpperBound => CookTemperature + CookTemperatureTolerance;
}
}
34 changes: 33 additions & 1 deletion S1API/Stations/ChemistryStationRecipeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ public sealed class ChemistryStationRecipeBuilder
{
private string? _title;
private int _cookTimeMinutes = 180;
private float _cookTemperature = 250f;
private float _cookTemperatureTolerance = 25f;
private Color _finalLiquidColor = Color.white;

private string? _productItemId;
private int _productQuantity = 1;
private S1ItemFramework.ItemDefinition? _productItem;
private QualityCalculationMethod _method = QualityCalculationMethod.Additive;

private readonly List<IngredientSpec> _ingredients = new List<IngredientSpec>();

Expand Down Expand Up @@ -127,6 +130,30 @@ public ChemistryStationRecipeBuilder WithProduct(string itemId, int quantity)
return this;
}

/// <summary>
/// Sets the quality calculation method.
/// </summary>
public ChemistryStationRecipeBuilder WithCalculationMethod(QualityCalculationMethod method)
{
_method = method;
return this;
}

/// <summary>
/// Sets the cook temperature and tolerance.
/// </summary>
public ChemistryStationRecipeBuilder WithTemperature(float cookTemperature, float tolerance)
{
if (cookTemperature <= 0)
throw new ArgumentOutOfRangeException(nameof(cookTemperature), "Cook temperature must be > 0.");
if (tolerance < 0)
throw new ArgumentOutOfRangeException(nameof(tolerance), "Cook temperature tolerance cannot be negative.");

_cookTemperature = cookTemperature;
_cookTemperatureTolerance = tolerance;
return this;
}

/// <summary>
/// Builds and auto-registers the recipe with S1API.
/// </summary>
Expand All @@ -147,9 +174,11 @@ public ChemistryStationRecipe Build()
recipeId: recipeId,
title: title,
cookTimeMinutes: _cookTimeMinutes,
temperature: new ChemistryStationRecipeTemperature(_cookTemperature, _cookTemperatureTolerance),
finalLiquidColor: _finalLiquidColor,
product: new ChemistryStationRecipeProduct(_productItemId!, _productQuantity),
ingredients: BuildIngredientWrappers());
ingredients: BuildIngredientWrappers(),
qualityCalculationMethod: _method);

return ChemistryStationRecipes.Register(wrapper);
}
Expand All @@ -167,7 +196,10 @@ internal S1StationFramework.StationRecipe BuildInternal()
recipe.Unlocked = true;
recipe.RecipeTitle = string.IsNullOrWhiteSpace(_title) ? _productItemId! : _title!;
recipe.CookTime_Mins = _cookTimeMinutes;
recipe.CookTemperature = _cookTemperature;
recipe.CookTemperatureTolerance = _cookTemperatureTolerance;
recipe.FinalLiquidColor = _finalLiquidColor;
// Other calculation methods do not exist in base game, so we implement them ourselves later
recipe.QualityCalculationMethod = S1StationFramework.StationRecipe.EQualityCalculationMethod.Additive;

recipe.Product = new S1StationFramework.StationRecipe.ItemQuantity
Expand Down
21 changes: 21 additions & 0 deletions S1API/Stations/QualityCalculationMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace S1API.Stations
{
/// <summary>
/// Calculation method to use when determining quality of
/// the <see cref="ChemistryStationRecipe"/> result.
/// </summary>
public enum QualityCalculationMethod
{
/// <summary>
/// Quality is calculated by adding the quality contributions of each ingredient together.
/// </summary>
Additive,
/// <summary>
/// Quality is determined by the product's default quality.
/// </summary>
/// <remarks>
/// Requires the result to be of type QualityItemInstance or its inheritor.
/// </remarks>
Absolute
}
}
Loading