Added RocketAssemblyStation and ReadyRocket
This commit is contained in:
+65
-19
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
public enum RocketAssemblyWorkstationState : byte
|
||||
{
|
||||
@@ -28,7 +29,9 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
|
||||
[Header("Interaction")]
|
||||
[SerializeField, Min(0.1f)] private float maximumTakeDistance = 3.5f;
|
||||
[SerializeField, Min(0.05f)] private float reinsertCooldownAfterTake = 0.5f;
|
||||
[FormerlySerializedAs("reinsertCooldownAfterTake")]
|
||||
[SerializeField, Min(0f)] private float minimumReinsertBlockDuration = 0.1f;
|
||||
[SerializeField, Min(0f)] private float reinsertClearance = 0.1f;
|
||||
|
||||
private readonly NetworkVariable<RocketAssemblyWorkstationState> state = new(
|
||||
RocketAssemblyWorkstationState.Loading,
|
||||
@@ -63,7 +66,8 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
private readonly NetworkItem[] slottedItems = new NetworkItem[MaximumSlotCount];
|
||||
private readonly IWorkstationItemControl[] slottedItemControls =
|
||||
new IWorkstationItemControl[MaximumSlotCount];
|
||||
private readonly Dictionary<ulong, double> reinsertBlockedUntil = new();
|
||||
private readonly Dictionary<ulong, ReinsertBlock> reinsertBlocks = new();
|
||||
private readonly List<ulong> clearedReinsertBlockIds = new();
|
||||
|
||||
private NetworkItem currentOutput;
|
||||
|
||||
@@ -170,6 +174,7 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
if (!IsServer || !IsSpawned)
|
||||
return;
|
||||
|
||||
UpdateReinsertBlocks();
|
||||
RemoveMissingSlottedItems();
|
||||
|
||||
if (State == RocketAssemblyWorkstationState.Assembling &&
|
||||
@@ -311,8 +316,11 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
NetworkItem item = slottedItems[slotIndex];
|
||||
if (item != null)
|
||||
{
|
||||
reinsertBlockedUntil[item.NetworkObjectId] =
|
||||
GetNetworkTime() + reinsertCooldownAfterTake;
|
||||
reinsertBlocks[item.NetworkObjectId] = new ReinsertBlock(
|
||||
item,
|
||||
slotIndex,
|
||||
item.GetComponentsInChildren<Collider>(true),
|
||||
GetNetworkTime() + minimumReinsertBlockDuration);
|
||||
}
|
||||
|
||||
ClearSlot(slotIndex);
|
||||
@@ -333,7 +341,8 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
|
||||
Array.Clear(slottedItems, 0, slottedItems.Length);
|
||||
Array.Clear(slottedItemControls, 0, slottedItemControls.Length);
|
||||
reinsertBlockedUntil.Clear();
|
||||
reinsertBlocks.Clear();
|
||||
clearedReinsertBlockIds.Clear();
|
||||
currentOutput = null;
|
||||
selectedRecipeIndex.Value = FindFirstValidRecipeIndex();
|
||||
assemblyStartedAt.Value = 0d;
|
||||
@@ -380,9 +389,7 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
}
|
||||
|
||||
bool defective = assemblyWillBeDefective.Value;
|
||||
ItemDefinition outputDefinition = defective
|
||||
? recipe.DefectiveOutputItem
|
||||
: recipe.OutputItem;
|
||||
RocketItemDefinition outputDefinition = recipe.OutputItem;
|
||||
if (outputDefinition == null || outputDefinition.WorldPrefab == null)
|
||||
{
|
||||
CancelAssemblyOnServer();
|
||||
@@ -408,12 +415,14 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
outputObject.Spawn();
|
||||
|
||||
currentOutput = outputObject.GetComponent<NetworkItem>();
|
||||
if (currentOutput == null ||
|
||||
NetworkRocketItem rocketItem = outputObject.GetComponent<NetworkRocketItem>();
|
||||
if (currentOutput == null || rocketItem == null ||
|
||||
!WorkstationItemControlUtility.TryGetControl(currentOutput, out _))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"{name} cannot produce '{outputDefinition.DisplayName}': the output prefab " +
|
||||
"must contain NetworkItem and a supported Carry or Drag interaction component.",
|
||||
"must contain NetworkItem, NetworkRocketItem and a supported Carry or Drag " +
|
||||
"interaction component.",
|
||||
this);
|
||||
outputObject.Despawn(true);
|
||||
currentOutput = null;
|
||||
@@ -421,6 +430,8 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
return;
|
||||
}
|
||||
|
||||
rocketItem.TryInitializeAssemblyQuality(defective);
|
||||
|
||||
assemblyStartedAt.Value = 0d;
|
||||
assemblyEndsAt.Value = 0d;
|
||||
state.Value = RocketAssemblyWorkstationState.OutputReady;
|
||||
@@ -513,17 +524,51 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
|
||||
private bool IsTemporarilyBlockedFromReinsert(NetworkItem item)
|
||||
{
|
||||
if (item == null ||
|
||||
!reinsertBlockedUntil.TryGetValue(item.NetworkObjectId, out double blockedUntil))
|
||||
return item != null && reinsertBlocks.ContainsKey(item.NetworkObjectId);
|
||||
}
|
||||
|
||||
private void UpdateReinsertBlocks()
|
||||
{
|
||||
if (reinsertBlocks.Count == 0)
|
||||
return;
|
||||
|
||||
double now = GetNetworkTime();
|
||||
clearedReinsertBlockIds.Clear();
|
||||
foreach (KeyValuePair<ulong, ReinsertBlock> pair in reinsertBlocks)
|
||||
{
|
||||
return false;
|
||||
ReinsertBlock block = pair.Value;
|
||||
bool canReleaseBlock = block.Item == null || !block.Item.IsSpawned ||
|
||||
(now >= block.MinimumBlockedUntil &&
|
||||
(!IsValidSlotIndex(block.SourceSlotIndex) ||
|
||||
!slotsBottomToTop[block.SourceSlotIndex].OverlapsAny(
|
||||
block.ItemColliders,
|
||||
reinsertClearance)));
|
||||
if (canReleaseBlock)
|
||||
clearedReinsertBlockIds.Add(pair.Key);
|
||||
}
|
||||
|
||||
if (GetNetworkTime() < blockedUntil)
|
||||
return true;
|
||||
foreach (ulong networkObjectId in clearedReinsertBlockIds)
|
||||
reinsertBlocks.Remove(networkObjectId);
|
||||
}
|
||||
|
||||
reinsertBlockedUntil.Remove(item.NetworkObjectId);
|
||||
return false;
|
||||
private readonly struct ReinsertBlock
|
||||
{
|
||||
public ReinsertBlock(
|
||||
NetworkItem item,
|
||||
int sourceSlotIndex,
|
||||
Collider[] itemColliders,
|
||||
double minimumBlockedUntil)
|
||||
{
|
||||
Item = item;
|
||||
SourceSlotIndex = sourceSlotIndex;
|
||||
ItemColliders = itemColliders;
|
||||
MinimumBlockedUntil = minimumBlockedUntil;
|
||||
}
|
||||
|
||||
public NetworkItem Item { get; }
|
||||
public int SourceSlotIndex { get; }
|
||||
public Collider[] ItemColliders { get; }
|
||||
public double MinimumBlockedUntil { get; }
|
||||
}
|
||||
|
||||
private NetworkItem ResolveSynchronizedItem(int slotIndex)
|
||||
@@ -588,8 +633,9 @@ public sealed class NetworkRocketAssemblyWorkstation : NetworkWorkstationControl
|
||||
slotIndex >= 0 && slotIndex < SlotCount && slotsBottomToTop[slotIndex] != null;
|
||||
|
||||
private static bool IsReadyRocketPart(NetworkItem item) =>
|
||||
item != null && item.IsSpawned && item.Definition != null &&
|
||||
item.Category == ItemCategory.RocketPart && item.Definition.IsAssemblyReady;
|
||||
item != null && item.IsSpawned &&
|
||||
item.TryGetDefinition(out PartItemDefinition definition) &&
|
||||
definition.IsReadyForAssembly;
|
||||
|
||||
private static bool IsRecipeValid(RocketAssemblyRecipe recipe) =>
|
||||
recipe != null && recipe.IsConfigured;
|
||||
|
||||
@@ -8,6 +8,7 @@ public sealed class RocketAssemblyInputSlot : MonoBehaviour, IInteractable
|
||||
[SerializeField] private Transform snapPoint;
|
||||
|
||||
private NetworkRocketAssemblyWorkstation workstation;
|
||||
private Collider triggerCollider;
|
||||
private int slotIndex = -1;
|
||||
|
||||
public Transform SnapPoint => snapPoint != null ? snapPoint : transform;
|
||||
@@ -21,10 +22,17 @@ public sealed class RocketAssemblyInputSlot : MonoBehaviour, IInteractable
|
||||
private void Reset()
|
||||
{
|
||||
snapPoint = transform;
|
||||
GetComponent<Collider>().isTrigger = true;
|
||||
ResolveTriggerCollider();
|
||||
triggerCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
private void OnValidate() => GetComponent<Collider>().isTrigger = true;
|
||||
private void Awake() => ResolveTriggerCollider();
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
ResolveTriggerCollider();
|
||||
triggerCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
public void Configure(NetworkRocketAssemblyWorkstation owner, int index)
|
||||
{
|
||||
@@ -51,4 +59,30 @@ public sealed class RocketAssemblyInputSlot : MonoBehaviour, IInteractable
|
||||
if (item != null)
|
||||
workstation.TryInsertItem(slotIndex, item);
|
||||
}
|
||||
|
||||
public bool OverlapsAny(Collider[] itemColliders, float clearance)
|
||||
{
|
||||
ResolveTriggerCollider();
|
||||
if (triggerCollider == null || !triggerCollider.enabled || itemColliders == null)
|
||||
return false;
|
||||
|
||||
Bounds triggerBounds = triggerCollider.bounds;
|
||||
triggerBounds.Expand(Mathf.Max(0f, clearance) * 2f);
|
||||
foreach (Collider itemCollider in itemColliders)
|
||||
{
|
||||
if (itemCollider != null && itemCollider.enabled &&
|
||||
triggerBounds.Intersects(itemCollider.bounds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ResolveTriggerCollider()
|
||||
{
|
||||
if (triggerCollider == null)
|
||||
triggerCollider = GetComponent<Collider>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,8 @@ public sealed class RocketAssemblyRecipe : ScriptableObject
|
||||
[SerializeField] private string recipeId;
|
||||
[SerializeField] private string displayName;
|
||||
[Tooltip("Exact assembly order. Element 0 is the lowest rocket part.")]
|
||||
[SerializeField] private List<ItemDefinition> requiredPartsBottomToTop = new();
|
||||
[SerializeField] private ItemDefinition outputItem;
|
||||
[SerializeField] private ItemDefinition defectiveOutputItem;
|
||||
[SerializeField] private List<PartItemDefinition> requiredPartsBottomToTop = new();
|
||||
[SerializeField] private RocketItemDefinition outputItem;
|
||||
[SerializeField, Min(0.05f)] private float assemblyDuration = 5f;
|
||||
|
||||
[Header("Blueprint")]
|
||||
@@ -23,10 +22,9 @@ public sealed class RocketAssemblyRecipe : ScriptableObject
|
||||
|
||||
public string RecipeId => recipeId;
|
||||
public string DisplayName => string.IsNullOrWhiteSpace(displayName) ? name : displayName;
|
||||
public IReadOnlyList<ItemDefinition> RequiredPartsBottomToTop => requiredPartsBottomToTop;
|
||||
public IReadOnlyList<PartItemDefinition> RequiredPartsBottomToTop => requiredPartsBottomToTop;
|
||||
public int RequiredPartCount => requiredPartsBottomToTop.Count;
|
||||
public ItemDefinition OutputItem => outputItem;
|
||||
public ItemDefinition DefectiveOutputItem => defectiveOutputItem;
|
||||
public RocketItemDefinition OutputItem => outputItem;
|
||||
public float AssemblyDuration => assemblyDuration;
|
||||
public string BlueprintTitle => string.IsNullOrWhiteSpace(blueprintTitle)
|
||||
? DisplayName
|
||||
@@ -36,8 +34,7 @@ public sealed class RocketAssemblyRecipe : ScriptableObject
|
||||
public bool IsConfigured =>
|
||||
requiredPartsBottomToTop.Count is > 0 and <= MaximumPartCount &&
|
||||
requiredPartsBottomToTop.TrueForAll(IsReadyRocketPart) &&
|
||||
IsSpawnable(outputItem) &&
|
||||
IsSpawnable(defectiveOutputItem);
|
||||
IsSpawnable(outputItem);
|
||||
|
||||
public bool Matches(IReadOnlyList<NetworkItem> slottedItems)
|
||||
{
|
||||
@@ -75,14 +72,12 @@ public sealed class RocketAssemblyRecipe : ScriptableObject
|
||||
requiredPartsBottomToTop.Count - MaximumPartCount);
|
||||
}
|
||||
|
||||
private static bool MatchesDefinition(NetworkItem item, ItemDefinition definition) =>
|
||||
private static bool MatchesDefinition(NetworkItem item, PartItemDefinition 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 IsReadyRocketPart(PartItemDefinition definition) =>
|
||||
definition != null && definition.IsReadyForAssembly;
|
||||
|
||||
private static bool IsSpawnable(ItemDefinition definition) =>
|
||||
definition != null && definition.WorldPrefab != null;
|
||||
|
||||
Reference in New Issue
Block a user