Added many other things and processings
This commit is contained in:
@@ -12,9 +12,10 @@ public enum DraggableMovementMode
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(Rigidbody), typeof(NetworkRigidbody))]
|
||||
public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
public sealed class NetworkDraggableInteractable : NetworkInteractable, IWorkstationItemControl
|
||||
{
|
||||
private const ulong NoDragger = ulong.MaxValue;
|
||||
private static readonly HashSet<NetworkDraggableInteractable> SpawnedDraggables = new();
|
||||
|
||||
[Header("Weight")]
|
||||
[SerializeField, Min(0.1f)] private float weight = 35f;
|
||||
@@ -59,6 +60,11 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly NetworkVariable<bool> isExternallyControlled = new(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly HashSet<Collider> ignoredPlayerColliders = new();
|
||||
private readonly RaycastHit[] groundHits = new RaycastHit[16];
|
||||
private float nextCollisionRefreshTime;
|
||||
@@ -79,6 +85,7 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
}
|
||||
public int RequiredDraggerCount => Mathf.Max(1, Mathf.CeilToInt(weight / pullStrengthPerPlayer));
|
||||
public bool HasEnoughPullStrength => ActiveDraggerCount >= RequiredDraggerCount;
|
||||
public bool IsExternallyControlled => isExternallyControlled.Value;
|
||||
|
||||
private bool HasAnyDragger => ActiveDraggerCount > 0;
|
||||
private bool IsDraggedByLocalPlayer =>
|
||||
@@ -88,6 +95,9 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsExternallyControlled)
|
||||
return "In machine";
|
||||
|
||||
if (IsDraggedByLocalPlayer)
|
||||
return "Release";
|
||||
|
||||
@@ -105,7 +115,11 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
}
|
||||
|
||||
public override bool IsInteractionAvailable => base.IsInteractionAvailable &&
|
||||
(IsDraggedByLocalPlayer || ActiveDraggerCount < maximumDraggers);
|
||||
!IsExternallyControlled &&
|
||||
(IsDraggedByLocalPlayer ||
|
||||
(ActiveDraggerCount < maximumDraggers &&
|
||||
(NetworkManager == null ||
|
||||
!TryGetDraggedObject(NetworkManager.LocalClientId, this, out _))));
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -121,15 +135,19 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
SpawnedDraggables.Add(this);
|
||||
firstDraggerClientId.OnValueChanged += HandleDraggerChanged;
|
||||
secondDraggerClientId.OnValueChanged += HandleDraggerChanged;
|
||||
isExternallyControlled.OnValueChanged += HandleExternalControlChanged;
|
||||
ApplyDraggingState();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
SpawnedDraggables.Remove(this);
|
||||
firstDraggerClientId.OnValueChanged -= HandleDraggerChanged;
|
||||
secondDraggerClientId.OnValueChanged -= HandleDraggerChanged;
|
||||
isExternallyControlled.OnValueChanged -= HandleExternalControlChanged;
|
||||
RestorePlayerCollisions();
|
||||
}
|
||||
|
||||
@@ -144,7 +162,7 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (!IsServer || !IsSpawned || physicsBody == null)
|
||||
if (!IsServer || !IsSpawned || physicsBody == null || IsExternallyControlled)
|
||||
return;
|
||||
|
||||
RemoveInvalidDraggers();
|
||||
@@ -177,9 +195,17 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanInteractOnServer(ulong senderClientId) =>
|
||||
IsDragger(senderClientId) ||
|
||||
ActiveDraggerCount < maximumDraggers;
|
||||
protected override bool CanInteractOnServer(ulong senderClientId)
|
||||
{
|
||||
if (IsExternallyControlled)
|
||||
return false;
|
||||
|
||||
if (IsDragger(senderClientId))
|
||||
return true;
|
||||
|
||||
return ActiveDraggerCount < maximumDraggers &&
|
||||
!TryGetDraggedObject(senderClientId, this, out _);
|
||||
}
|
||||
|
||||
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
|
||||
{
|
||||
@@ -191,6 +217,9 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
|
||||
private void AddDragger(ulong clientId)
|
||||
{
|
||||
if (IsExternallyControlled || TryGetDraggedObject(clientId, this, out _))
|
||||
return;
|
||||
|
||||
if (firstDraggerClientId.Value == NoDragger)
|
||||
firstDraggerClientId.Value = clientId;
|
||||
else if (maximumDraggers > 1 && secondDraggerClientId.Value == NoDragger)
|
||||
@@ -265,19 +294,78 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
if (clientId == NoDragger || !TryGetPlayerObject(clientId, out NetworkObject playerObject))
|
||||
return;
|
||||
|
||||
Transform dragAnchor = ResolveDragAnchor(playerObject);
|
||||
target += dragAnchor.TransformPoint(dragOffset);
|
||||
forward += dragAnchor.forward;
|
||||
FirstPersonCameraController cameraController =
|
||||
playerObject.GetComponent<FirstPersonCameraController>();
|
||||
Vector3 aimForward = cameraController != null
|
||||
? cameraController.AimForward
|
||||
: playerObject.transform.forward;
|
||||
|
||||
target += playerObject.transform.position +
|
||||
playerObject.transform.right * dragOffset.x +
|
||||
Vector3.up * dragOffset.y +
|
||||
aimForward * dragOffset.z;
|
||||
forward += aimForward;
|
||||
validTargets++;
|
||||
}
|
||||
|
||||
private static Transform ResolveDragAnchor(NetworkObject playerObject)
|
||||
private static bool TryGetDraggedObject(
|
||||
ulong clientId,
|
||||
NetworkDraggableInteractable except,
|
||||
out NetworkDraggableInteractable draggedObject)
|
||||
{
|
||||
FirstPersonCameraController cameraController =
|
||||
playerObject.GetComponent<FirstPersonCameraController>();
|
||||
return cameraController != null
|
||||
? cameraController.AimTransform
|
||||
: playerObject.transform;
|
||||
foreach (NetworkDraggableInteractable candidate in SpawnedDraggables)
|
||||
{
|
||||
if (candidate != null && candidate != except && candidate.IsDragger(clientId))
|
||||
{
|
||||
draggedObject = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
draggedObject = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetDraggedObject(
|
||||
ulong clientId,
|
||||
out NetworkDraggableInteractable draggedObject) =>
|
||||
TryGetDraggedObject(clientId, null, out draggedObject);
|
||||
|
||||
public void RequestRelease()
|
||||
{
|
||||
if (IsSpawned && IsDraggedByLocalPlayer)
|
||||
RequestReleaseRpc();
|
||||
}
|
||||
|
||||
public bool TryAcquireExternalControl()
|
||||
{
|
||||
if (!IsServer || !IsSpawned || IsExternallyControlled)
|
||||
return false;
|
||||
|
||||
isExternallyControlled.Value = true;
|
||||
|
||||
firstDraggerClientId.Value = NoDragger;
|
||||
secondDraggerClientId.Value = NoDragger;
|
||||
ApplyDraggingState();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryReleaseExternalControl()
|
||||
{
|
||||
if (!IsServer || !IsSpawned || !IsExternallyControlled)
|
||||
return false;
|
||||
|
||||
isExternallyControlled.Value = false;
|
||||
ApplyDraggingState();
|
||||
return true;
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestReleaseRpc(RpcParams rpcParams = default)
|
||||
{
|
||||
ulong senderClientId = rpcParams.Receive.SenderClientId;
|
||||
if (!IsExternallyControlled && IsDragger(senderClientId))
|
||||
RemoveDragger(senderClientId);
|
||||
}
|
||||
|
||||
private bool IsDragger(ulong clientId) =>
|
||||
@@ -285,11 +373,13 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
|
||||
private void HandleDraggerChanged(ulong _, ulong __) => ApplyDraggingState();
|
||||
|
||||
private void HandleExternalControlChanged(bool _, bool __) => ApplyDraggingState();
|
||||
|
||||
private void ApplyDraggingState()
|
||||
{
|
||||
if (physicsBody != null)
|
||||
{
|
||||
bool shouldBeKinematic = HasAnyDragger || !IsServer;
|
||||
bool shouldBeKinematic = HasAnyDragger || IsExternallyControlled || !IsServer;
|
||||
|
||||
if (physicsBody.isKinematic && !shouldBeKinematic)
|
||||
physicsBody.isKinematic = false;
|
||||
@@ -303,7 +393,7 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
physicsBody.isKinematic = shouldBeKinematic;
|
||||
}
|
||||
|
||||
if (HasAnyDragger)
|
||||
if (HasAnyDragger || IsExternallyControlled)
|
||||
{
|
||||
dragVelocity = Vector3.zero;
|
||||
CacheGroundContactPoint();
|
||||
|
||||
@@ -4,7 +4,7 @@ using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody), typeof(NetworkRigidbody))]
|
||||
public abstract class NetworkHeldInteractable : NetworkInteractable
|
||||
public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstationItemControl
|
||||
{
|
||||
protected const ulong NoHolder = ulong.MaxValue;
|
||||
private static readonly HashSet<NetworkHeldInteractable> SpawnedHeldObjects = new();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
public interface IWorkstationItemControl
|
||||
{
|
||||
bool TryAcquireExternalControl();
|
||||
bool TryReleaseExternalControl();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b32c69f6645bac8499f4e8e068e84034
|
||||
@@ -16,12 +16,22 @@ public sealed class PlayerCarryController : NetworkBehaviour, IPlayerSystem
|
||||
if (!isInitialized || !IsOwner || Keyboard.current == null)
|
||||
return;
|
||||
|
||||
if (Keyboard.current.gKey.wasPressedThisFrame &&
|
||||
NetworkHeldInteractable.TryGetHeldObject(
|
||||
if (!Keyboard.current.gKey.wasPressedThisFrame)
|
||||
return;
|
||||
|
||||
if (NetworkHeldInteractable.TryGetHeldObject(
|
||||
OwnerClientId,
|
||||
out NetworkHeldInteractable heldObject))
|
||||
{
|
||||
heldObject.RequestThrow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkDraggableInteractable.TryGetDraggedObject(
|
||||
OwnerClientId,
|
||||
out NetworkDraggableInteractable draggedObject))
|
||||
{
|
||||
draggedObject.RequestRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,116 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public sealed class PlayerSpawnController : NetworkBehaviour, IPlayerSystem
|
||||
{
|
||||
[Header("Random spawn area")]
|
||||
[SerializeField] private Vector3 spawnCenter = new(0f, 1f, 0f);
|
||||
[SerializeField, Min(0f)] private float spawnRadius = 8f;
|
||||
[Header("Start Room Spawn")]
|
||||
[SerializeField, Min(0f)] private float edgePadding = 0.75f;
|
||||
[SerializeField, Min(0f)] private float heightAboveFloor = 0.15f;
|
||||
|
||||
private readonly NetworkVariable<Vector3> assignedSpawnPosition = new(
|
||||
Vector3.zero,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly NetworkVariable<bool> hasAssignedSpawnPosition = new(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private int assignedSceneHandle = -1;
|
||||
|
||||
public void Initialize(PlayerFacade facade) { }
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
assignedSpawnPosition.OnValueChanged += HandleSpawnPositionChanged;
|
||||
hasAssignedSpawnPosition.OnValueChanged += HandleSpawnAssignmentChanged;
|
||||
SceneManager.sceneLoaded += HandleSceneLoaded;
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
Vector2 randomOffset = Random.insideUnitCircle * spawnRadius;
|
||||
assignedSpawnPosition.Value = spawnCenter + new Vector3(randomOffset.x, 0f, randomOffset.y);
|
||||
}
|
||||
TryAssignStartRoomPosition(SceneManager.GetActiveScene());
|
||||
|
||||
ApplySpawnPosition(assignedSpawnPosition.Value);
|
||||
if (hasAssignedSpawnPosition.Value)
|
||||
ApplySpawnPosition(assignedSpawnPosition.Value);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
assignedSpawnPosition.OnValueChanged -= HandleSpawnPositionChanged;
|
||||
hasAssignedSpawnPosition.OnValueChanged -= HandleSpawnAssignmentChanged;
|
||||
SceneManager.sceneLoaded -= HandleSceneLoaded;
|
||||
}
|
||||
|
||||
private void HandleSpawnPositionChanged(Vector3 _, Vector3 position) =>
|
||||
ApplySpawnPosition(position);
|
||||
private void HandleSceneLoaded(Scene scene, LoadSceneMode _)
|
||||
{
|
||||
if (IsServer)
|
||||
TryAssignStartRoomPosition(scene);
|
||||
|
||||
if (IsOwner && hasAssignedSpawnPosition.Value)
|
||||
ApplySpawnPosition(assignedSpawnPosition.Value);
|
||||
}
|
||||
|
||||
private void TryAssignStartRoomPosition(Scene scene)
|
||||
{
|
||||
if (!scene.IsValid() || !scene.isLoaded || scene.handle == assignedSceneHandle)
|
||||
return;
|
||||
|
||||
StartRoomZone startRoom = FindStartRoomInScene(scene);
|
||||
if (startRoom == null ||
|
||||
!startRoom.TryGetRandomSpawnPosition(
|
||||
edgePadding,
|
||||
heightAboveFloor,
|
||||
out Vector3 position))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasAssignedSpawnPosition.Value = false;
|
||||
assignedSpawnPosition.Value = position;
|
||||
hasAssignedSpawnPosition.Value = true;
|
||||
assignedSceneHandle = scene.handle;
|
||||
}
|
||||
|
||||
private static StartRoomZone FindStartRoomInScene(Scene scene)
|
||||
{
|
||||
foreach (GameObject rootObject in scene.GetRootGameObjects())
|
||||
{
|
||||
StartRoomZone startRoom = rootObject.GetComponentInChildren<StartRoomZone>(true);
|
||||
if (startRoom != null)
|
||||
return startRoom;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void HandleSpawnPositionChanged(Vector3 _, Vector3 position)
|
||||
{
|
||||
if (hasAssignedSpawnPosition.Value)
|
||||
ApplySpawnPosition(position);
|
||||
}
|
||||
|
||||
private void HandleSpawnAssignmentChanged(bool _, bool isAssigned)
|
||||
{
|
||||
if (isAssigned)
|
||||
ApplySpawnPosition(assignedSpawnPosition.Value);
|
||||
}
|
||||
|
||||
private void ApplySpawnPosition(Vector3 position)
|
||||
{
|
||||
if (IsOwner)
|
||||
transform.position = position;
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
CharacterController characterController = GetComponent<CharacterController>();
|
||||
bool restoreCharacterController = characterController != null && characterController.enabled;
|
||||
|
||||
if (restoreCharacterController)
|
||||
characterController.enabled = false;
|
||||
|
||||
transform.position = position;
|
||||
Physics.SyncTransforms();
|
||||
|
||||
if (restoreCharacterController)
|
||||
characterController.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ public sealed class StartRoomZone : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Collider roomCollider;
|
||||
|
||||
public Collider RoomCollider => roomCollider;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (roomCollider == null)
|
||||
@@ -14,6 +16,56 @@ public sealed class StartRoomZone : MonoBehaviour
|
||||
public bool Contains(Vector3 worldPosition) =>
|
||||
roomCollider != null && roomCollider.bounds.Contains(worldPosition);
|
||||
|
||||
public bool TryGetRandomSpawnPosition(
|
||||
float edgePadding,
|
||||
float heightAboveFloor,
|
||||
out Vector3 position)
|
||||
{
|
||||
if (roomCollider == null)
|
||||
{
|
||||
position = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
edgePadding = Mathf.Max(0f, edgePadding);
|
||||
heightAboveFloor = Mathf.Max(0f, heightAboveFloor);
|
||||
|
||||
if (roomCollider is BoxCollider boxCollider)
|
||||
{
|
||||
Vector3 lossyScale = boxCollider.transform.lossyScale;
|
||||
float localPaddingX = edgePadding / Mathf.Max(Mathf.Abs(lossyScale.x), 0.0001f);
|
||||
float localPaddingZ = edgePadding / Mathf.Max(Mathf.Abs(lossyScale.z), 0.0001f);
|
||||
Vector3 halfSize = boxCollider.size * 0.5f;
|
||||
float availableX = Mathf.Max(0f, halfSize.x - localPaddingX);
|
||||
float availableZ = Mathf.Max(0f, halfSize.z - localPaddingZ);
|
||||
Vector3 localPosition = boxCollider.center + new Vector3(
|
||||
Random.Range(-availableX, availableX),
|
||||
-halfSize.y,
|
||||
Random.Range(-availableZ, availableZ));
|
||||
|
||||
position = boxCollider.transform.TransformPoint(localPosition) +
|
||||
Vector3.up * heightAboveFloor;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bounds bounds = roomCollider.bounds;
|
||||
float minX = bounds.min.x + edgePadding;
|
||||
float maxX = bounds.max.x - edgePadding;
|
||||
float minZ = bounds.min.z + edgePadding;
|
||||
float maxZ = bounds.max.z - edgePadding;
|
||||
|
||||
if (minX > maxX)
|
||||
minX = maxX = bounds.center.x;
|
||||
if (minZ > maxZ)
|
||||
minZ = maxZ = bounds.center.z;
|
||||
|
||||
position = new Vector3(
|
||||
Random.Range(minX, maxX),
|
||||
bounds.min.y + heightAboveFloor,
|
||||
Random.Range(minZ, maxZ));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Collider targetCollider = roomCollider != null ? roomCollider : GetComponent<Collider>();
|
||||
|
||||
+92
-18
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -11,8 +12,8 @@ public enum ItemProcessingWorkstationState : byte
|
||||
|
||||
public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationController
|
||||
{
|
||||
[Header("Recipe")]
|
||||
[SerializeField] private ItemProcessingRecipe recipe;
|
||||
[Header("Recipes")]
|
||||
[SerializeField] private List<ItemProcessingRecipe> recipes = new();
|
||||
|
||||
[Header("Slot")]
|
||||
[SerializeField] private Transform inputPoint;
|
||||
@@ -32,16 +33,27 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly NetworkVariable<int> activeRecipeIndex = new(
|
||||
-1,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private NetworkItem currentItem;
|
||||
private IWorkstationItemControl currentItemControl;
|
||||
|
||||
public override bool IsOperating => State == ItemProcessingWorkstationState.Processing;
|
||||
public ItemProcessingWorkstationState State => state.Value;
|
||||
public ItemProcessingRecipe Recipe => recipe;
|
||||
public IReadOnlyList<ItemProcessingRecipe> Recipes => recipes;
|
||||
public int ActiveRecipeIndex => activeRecipeIndex.Value;
|
||||
public ItemProcessingRecipe ActiveRecipe => TryGetRecipe(
|
||||
activeRecipeIndex.Value,
|
||||
out ItemProcessingRecipe recipe)
|
||||
? recipe
|
||||
: null;
|
||||
public NetworkItem CurrentItem => currentItem;
|
||||
public bool CanAcceptItem => IsServer && IsSpawned &&
|
||||
State == ItemProcessingWorkstationState.Empty &&
|
||||
recipe != null && recipe.InputItem != null &&
|
||||
recipe.OutputItem != null && recipe.OutputItem.WorldPrefab != null;
|
||||
recipes.Exists(IsRecipeValid);
|
||||
|
||||
public float ProcessProgress
|
||||
{
|
||||
@@ -72,6 +84,7 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
state.OnValueChanged += HandleStateChanged;
|
||||
processStartedAt.OnValueChanged += HandleProcessTimeChanged;
|
||||
processEndsAt.OnValueChanged += HandleProcessTimeChanged;
|
||||
activeRecipeIndex.OnValueChanged += HandleActiveRecipeChanged;
|
||||
|
||||
if (IsServer)
|
||||
ResetWorkstationOnServer();
|
||||
@@ -84,6 +97,7 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
state.OnValueChanged -= HandleStateChanged;
|
||||
processStartedAt.OnValueChanged -= HandleProcessTimeChanged;
|
||||
processEndsAt.OnValueChanged -= HandleProcessTimeChanged;
|
||||
activeRecipeIndex.OnValueChanged -= HandleActiveRecipeChanged;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -123,15 +137,19 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
|
||||
public bool TryAcceptItem(NetworkItem item)
|
||||
{
|
||||
if (!CanAcceptItem || item == null || !item.IsSpawned || !recipe.Accepts(item))
|
||||
if (!CanAcceptItem || item == null || !item.IsSpawned ||
|
||||
!TryFindRecipe(item, out int recipeIndex, out ItemProcessingRecipe recipe) ||
|
||||
!TryGetItemControl(item, out IWorkstationItemControl itemControl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkCarryableInteractable carryable =
|
||||
item.GetComponent<NetworkCarryableInteractable>();
|
||||
if (carryable == null || !carryable.TryAcquireExternalControl())
|
||||
if (!itemControl.TryAcquireExternalControl())
|
||||
return false;
|
||||
|
||||
currentItem = item;
|
||||
currentItemControl = itemControl;
|
||||
activeRecipeIndex.Value = recipeIndex;
|
||||
SnapToInputPoint(item.transform);
|
||||
|
||||
double now = GetNetworkTime();
|
||||
@@ -156,6 +174,7 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
|
||||
private void CompleteProcessingOnServer()
|
||||
{
|
||||
ItemProcessingRecipe recipe = ActiveRecipe;
|
||||
if (currentItem == null || recipe == null ||
|
||||
recipe.OutputItem == null || recipe.OutputItem.WorldPrefab == null)
|
||||
{
|
||||
@@ -169,6 +188,7 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
|
||||
NetworkObject inputObject = currentItem.NetworkObject;
|
||||
currentItem = null;
|
||||
currentItemControl = null;
|
||||
|
||||
if (inputObject != null && inputObject.IsSpawned)
|
||||
inputObject.Despawn(true);
|
||||
@@ -180,13 +200,12 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
outputObject.Spawn();
|
||||
|
||||
currentItem = outputObject.GetComponent<NetworkItem>();
|
||||
if (currentItem == null ||
|
||||
outputObject.GetComponent<NetworkCarryableInteractable>() == null)
|
||||
if (currentItem == null || !TryGetItemControl(currentItem, out _))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"{name} cannot produce '{recipe.OutputItem.DisplayName}': " +
|
||||
"the output prefab must contain NetworkItem and " +
|
||||
"NetworkCarryableInteractable.",
|
||||
"the output prefab must contain NetworkItem and a supported " +
|
||||
"Carry or Drag interaction component.",
|
||||
this);
|
||||
outputObject.Despawn(true);
|
||||
currentItem = null;
|
||||
@@ -205,13 +224,12 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
if (State == ItemProcessingWorkstationState.Processing && currentItem != null &&
|
||||
currentItem.TryGetComponent(out NetworkCarryableInteractable carryable))
|
||||
{
|
||||
carryable.TryReleaseExternalControl();
|
||||
}
|
||||
if (State == ItemProcessingWorkstationState.Processing && currentItemControl != null)
|
||||
currentItemControl.TryReleaseExternalControl();
|
||||
|
||||
currentItem = null;
|
||||
currentItemControl = null;
|
||||
activeRecipeIndex.Value = -1;
|
||||
processStartedAt.Value = 0d;
|
||||
processEndsAt.Value = 0d;
|
||||
state.Value = ItemProcessingWorkstationState.Empty;
|
||||
@@ -233,4 +251,60 @@ public sealed class NetworkItemProcessingWorkstation : NetworkWorkstationControl
|
||||
ItemProcessingWorkstationState __) => Changed?.Invoke();
|
||||
|
||||
private void HandleProcessTimeChanged(double _, double __) => Changed?.Invoke();
|
||||
|
||||
private void HandleActiveRecipeChanged(int _, int __) => Changed?.Invoke();
|
||||
|
||||
private bool TryFindRecipe(
|
||||
NetworkItem item,
|
||||
out int recipeIndex,
|
||||
out ItemProcessingRecipe recipe)
|
||||
{
|
||||
for (int index = 0; index < recipes.Count; index++)
|
||||
{
|
||||
ItemProcessingRecipe candidate = recipes[index];
|
||||
if (IsRecipeValid(candidate) && candidate.Accepts(item))
|
||||
{
|
||||
recipeIndex = index;
|
||||
recipe = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
recipeIndex = -1;
|
||||
recipe = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetRecipe(int index, out ItemProcessingRecipe recipe)
|
||||
{
|
||||
if (index >= 0 && index < recipes.Count && IsRecipeValid(recipes[index]))
|
||||
{
|
||||
recipe = recipes[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
recipe = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRecipeValid(ItemProcessingRecipe recipe) =>
|
||||
recipe != null && recipe.InputItem != null &&
|
||||
recipe.OutputItem != null && recipe.OutputItem.WorldPrefab != null;
|
||||
|
||||
private static bool TryGetItemControl(
|
||||
NetworkItem item,
|
||||
out IWorkstationItemControl itemControl)
|
||||
{
|
||||
foreach (MonoBehaviour component in item.GetComponents<MonoBehaviour>())
|
||||
{
|
||||
if (component is IWorkstationItemControl candidate)
|
||||
{
|
||||
itemControl = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
itemControl = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user