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
57 changes: 57 additions & 0 deletions S1API/Items/Additive/AdditiveDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#if (IL2CPPMELON)
using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
using S1ItemFramework = ScheduleOne.ItemFramework;
#endif
using System;
using UnityEngine;

namespace S1API.Items.Additive
{
/// <summary>
/// Represents an additive item definition.
/// Extends <see cref="StorableItemDefinition"/> with additive-specific properties.
/// </summary>
/// <remarks>
/// Builder-only: these properties are intentionally read-only to avoid runtime surprises from mutating
/// globally-registered ScriptableObject definitions mid-session. Use <see cref="AdditiveItemCreator"/> to create
/// additives with configured effects.
/// </remarks>
public sealed class AdditiveDefinition : Storable.StorableItemDefinition
{
/// <summary>
/// INTERNAL: Wraps an existing native additive definition.
/// </summary>
internal AdditiveDefinition(S1ItemFramework.AdditiveDefinition definition)
: base(definition)
{
S1AdditiveDefinition = definition;
}

/// <summary>
/// INTERNAL: A reference to the native game additive definition.
/// </summary>
internal S1ItemFramework.AdditiveDefinition S1AdditiveDefinition { get; }

/// <summary>
/// Display material used for the additive (if applicable).
/// </summary>
public Material DisplayMaterial => S1AdditiveDefinition.DisplayMaterial;

/// <summary>
/// Quality modifier applied by this additive.
/// </summary>
public float QualityChange => S1AdditiveDefinition.QualityChange;

/// <summary>
/// Yield multiplier applied by this additive.
/// </summary>
public float YieldMultiplier => S1AdditiveDefinition.YieldMultiplier;

/// <summary>
/// Instant growth fraction applied by this additive (0..1).
/// </summary>
public float InstantGrowth => S1AdditiveDefinition.InstantGrowth;
}
}

136 changes: 136 additions & 0 deletions S1API/Items/Additive/AdditiveDefinitionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#if (IL2CPPMELON)
using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
using S1CoreItemFramework = Il2CppScheduleOne.Core.Items.Framework;
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
using S1ItemFramework = ScheduleOne.ItemFramework;
using S1CoreItemFramework = ScheduleOne.Core.Items.Framework;
#endif
using S1API.Internal.Utils;
using S1API.Items.Storable;
using S1API.Logging;
using UnityEngine;

namespace S1API.Items.Additive
{
/// <summary>
/// Builder for composing additive definitions at runtime.
/// Use fluent methods to configure additive properties before calling <see cref="Build"/>.
/// </summary>
public sealed class AdditiveDefinitionBuilder
: StorableItemDefinitionBuilderBase<AdditiveDefinitionBuilder>
{
private static readonly Log Logger = new Log("AdditiveDefinitionBuilder");

private S1ItemFramework.AdditiveDefinition AdditiveDefinition =>
CrossType.As<S1ItemFramework.AdditiveDefinition>(Definition);

/// <summary>
/// INTERNAL: Creates a new builder instance with a fresh AdditiveDefinition.
/// Only <see cref="AdditiveItemCreator"/> can instantiate this.
/// </summary>
internal AdditiveDefinitionBuilder()
: base(ScriptableObject.CreateInstance<S1ItemFramework.AdditiveDefinition>)
{
Definition.Category = S1CoreItemFramework.EItemCategory.Agriculture;
}

/// <summary>
/// INTERNAL: Creates a builder instance initialized by cloning an existing additive.
/// </summary>
internal AdditiveDefinitionBuilder(
S1ItemFramework.AdditiveDefinition source)
: base(source,
ScriptableObject.CreateInstance<S1ItemFramework.AdditiveDefinition>)
{
}

/// <inheritdoc/>
protected override void CopyPropertiesFrom(
S1ItemFramework.StorableItemDefinition source)
{
base.CopyPropertiesFrom(source);

var additiveSource = CrossType.As<S1ItemFramework.AdditiveDefinition>(source);

// AdditiveDefinition properties (auto-properties with private set in Mono)
AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.DisplayMaterial),
additiveSource.DisplayMaterial);
AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.QualityChange),
additiveSource.QualityChange);
AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.YieldMultiplier),
additiveSource.YieldMultiplier);
AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.InstantGrowth),
additiveSource.InstantGrowth);
}

/// <summary>
/// Sets the display material for this additive.
/// </summary>
public AdditiveDefinitionBuilder WithDisplayMaterial(Material material)
{
if (!AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.DisplayMaterial),
material))
{
Logger.Warning(
$"Failed to set DisplayMaterial on AdditiveDefinition '{AdditiveDefinition.ID ?? "<no id>"}'.");
}

return this;
}

/// <summary>
/// Sets the effect values for this additive.
/// </summary>
public AdditiveDefinitionBuilder WithEffects(float yieldMultiplier, float instantGrowth, float qualityChange)
{
instantGrowth = Mathf.Clamp01(instantGrowth);
if (!AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.YieldMultiplier),
yieldMultiplier))
{
Logger.Warning(
$"Failed to set YieldMultiplier on AdditiveDefinition '{AdditiveDefinition.ID ?? "<no id>"}'.");
}

if (!AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.InstantGrowth),
instantGrowth))
{
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Logger.Warning(
$"Failed to set InstantGrowth on AdditiveDefinition '{AdditiveDefinition.ID ?? "<no id>"}'.");
}

if (!AutoPropertySetter.TrySet(AdditiveDefinition, nameof(S1ItemFramework.AdditiveDefinition.QualityChange),
qualityChange))
{
Logger.Warning(
$"Failed to set QualityChange on AdditiveDefinition '{AdditiveDefinition.ID ?? "<no id>"}'.");
}

return this;
}

/// <summary>
/// Builds the item definition, registers it with the game's registry, and returns a wrapper.
/// </summary>
/// <returns>A wrapper around the created additive definition.</returns>
public new AdditiveDefinition Build()
{
return (AdditiveDefinition)base.Build();
}

/// <summary>
/// INTERNAL: Builds and returns the raw game item definition without registering.
/// Used internally by S1API. Modders should use <see cref="Build"/> instead.
/// </summary>
internal new S1ItemFramework.AdditiveDefinition BuildInternal()
{
return AdditiveDefinition;
}

/// <inheritdoc />
protected override Storable.StorableItemDefinition CreateWrapper(
S1ItemFramework.StorableItemDefinition definition)
{
return new AdditiveDefinition(AdditiveDefinition);
}
}
}
71 changes: 71 additions & 0 deletions S1API/Items/Additive/AdditiveItemCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#if (IL2CPPMELON)
using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
using S1Registry = Il2CppScheduleOne.Registry;
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
using S1ItemFramework = ScheduleOne.ItemFramework;
using S1Registry = ScheduleOne.Registry;
#endif
using System;
using S1API.Internal.Utils;

namespace S1API.Items.Additive
{
/// <summary>
/// Provides convenient static methods for creating custom additive items.
/// Use <see cref="CreateBuilder"/> for creating additives from scratch, or <see cref="CloneFrom"/> for variants.
/// </summary>
public static class AdditiveItemCreator
{
/// <summary>
/// Creates a new builder for composing an additive definition with full flexibility.
/// Use fluent methods to configure the additive, then call Build() to register it.
/// </summary>
public static AdditiveDefinitionBuilder CreateBuilder()
{
return new AdditiveDefinitionBuilder();
}

/// <summary>
/// Creates a new additive builder by cloning an existing additive by ID.
/// </summary>
/// <param name="sourceItemId">The ID of the additive to clone.</param>
/// <returns>A builder pre-configured with the source additive's properties.</returns>
/// <exception cref="ArgumentException">Thrown if the source item does not exist or is not an additive.</exception>
public static AdditiveDefinitionBuilder CloneFrom(string sourceItemId)
{
if (string.IsNullOrWhiteSpace(sourceItemId))
{
throw new ArgumentException("Source item ID cannot be null or whitespace", nameof(sourceItemId));
}

var sourceDefinition = S1Registry.GetItem(sourceItemId);
if (sourceDefinition == null)
{
throw new ArgumentException($"Source item with ID '{sourceItemId}' not found in registry", nameof(sourceItemId));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

if (!CrossType.Is(sourceDefinition, out S1ItemFramework.AdditiveDefinition additiveDef))
{
throw new ArgumentException($"Item '{sourceItemId}' is not an AdditiveDefinition", nameof(sourceItemId));
}

return new AdditiveDefinitionBuilder(additiveDef);
}

/// <summary>
/// Creates a new additive builder by cloning an existing additive wrapper.
/// </summary>
/// <param name="source">The additive definition to clone from.</param>
/// <returns>A builder pre-configured with the source additive's properties.</returns>
public static AdditiveDefinitionBuilder CloneFrom(AdditiveDefinition source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source), "Source additive definition cannot be null");
}

return new AdditiveDefinitionBuilder(source.S1AdditiveDefinition);
}
}
}

2 changes: 2 additions & 0 deletions S1API/Items/AdditiveDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using S1ItemFramework = ScheduleOne.ItemFramework;
#endif

using System;
using UnityEngine;

namespace S1API.Items
Expand All @@ -17,6 +18,7 @@ namespace S1API.Items
/// globally-registered ScriptableObject definitions mid-session. Use <see cref="AdditiveItemCreator"/> to create
/// additives with configured effects.
/// </remarks>
[Obsolete("Use S1API.Items.Additive.AdditiveDefinition instead")]
public sealed class AdditiveDefinition : StorableItemDefinition
{
/// <summary>
Expand Down
1 change: 1 addition & 0 deletions S1API/Items/AdditiveDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace S1API.Items
/// Builder for composing additive definitions at runtime.
/// Use fluent methods to configure additive properties before calling <see cref="Build"/>.
/// </summary>
[Obsolete("Use S1API.Items.Additive.AdditiveDefinitionBuilder instead")]
public sealed class AdditiveDefinitionBuilder
{
private static readonly Log Logger = new Log("AdditiveDefinitionBuilder");
Expand Down
1 change: 1 addition & 0 deletions S1API/Items/AdditiveItemCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace S1API.Items
/// Provides convenient static methods for creating custom additive items.
/// Use <see cref="CreateBuilder"/> for creating additives from scratch, or <see cref="CloneFrom"/> for variants.
/// </summary>
[Obsolete("Use S1API.Items.Additive.AdditiveItemCreator instead")]
public static class AdditiveItemCreator
{
/// <summary>
Expand Down
Loading
Loading