using System.Collections.Generic; using UnityEngine; [CreateAssetMenu( fileName = "RocketAssemblyRecipe", menuName = "FORT-BO/Workstations/Rocket Assembly Recipe")] public sealed class RocketAssemblyRecipe : ScriptableObject { public const int MaximumPartCount = 9; [Header("Recipe")] [SerializeField] private string recipeId; [SerializeField] private string displayName; [Tooltip("Exact assembly order. Element 0 is the lowest rocket part.")] [SerializeField] private List requiredPartsBottomToTop = new(); [SerializeField] private ItemDefinition outputItem; [SerializeField] private ItemDefinition defectiveOutputItem; [SerializeField, Min(0.05f)] private float assemblyDuration = 5f; [Header("Blueprint")] [SerializeField] private string blueprintTitle; [SerializeField] private Sprite blueprintImage; public string RecipeId => recipeId; public string DisplayName => string.IsNullOrWhiteSpace(displayName) ? name : displayName; public IReadOnlyList RequiredPartsBottomToTop => requiredPartsBottomToTop; public int RequiredPartCount => requiredPartsBottomToTop.Count; public ItemDefinition OutputItem => outputItem; public ItemDefinition DefectiveOutputItem => defectiveOutputItem; public float AssemblyDuration => assemblyDuration; public string BlueprintTitle => string.IsNullOrWhiteSpace(blueprintTitle) ? DisplayName : blueprintTitle; public Sprite BlueprintImage => blueprintImage; public bool IsConfigured => requiredPartsBottomToTop.Count is > 0 and <= MaximumPartCount && requiredPartsBottomToTop.TrueForAll(IsReadyRocketPart) && IsSpawnable(outputItem) && IsSpawnable(defectiveOutputItem); public bool Matches(IReadOnlyList slottedItems) { if (!IsConfigured || slottedItems == null || slottedItems.Count < requiredPartsBottomToTop.Count) { return false; } for (int index = 0; index < requiredPartsBottomToTop.Count; index++) { NetworkItem item = slottedItems[index]; if (item == null || !MatchesDefinition(item, requiredPartsBottomToTop[index])) return false; } for (int index = requiredPartsBottomToTop.Count; index < slottedItems.Count; index++) { if (slottedItems[index] != null) return false; } return true; } private void OnValidate() { recipeId = recipeId?.Trim().ToLowerInvariant(); displayName = displayName?.Trim(); blueprintTitle = blueprintTitle?.Trim(); if (requiredPartsBottomToTop.Count > MaximumPartCount) requiredPartsBottomToTop.RemoveRange( MaximumPartCount, requiredPartsBottomToTop.Count - MaximumPartCount); } private static bool MatchesDefinition(NetworkItem item, ItemDefinition definition) => item.Definition == definition || (!string.IsNullOrEmpty(definition.ItemId) && item.ItemId == definition.ItemId); private static bool IsReadyRocketPart(ItemDefinition definition) => definition != null && definition.Category == ItemCategory.RocketPart && definition.IsAssemblyReady; private static bool IsSpawnable(ItemDefinition definition) => definition != null && definition.WorldPrefab != null; }