From 5804b170d03d824a7207f45065a6ccd338be1ccc Mon Sep 17 00:00:00 2001 From: Michal Pikulski Date: Mon, 8 Sep 2025 16:13:13 +0200 Subject: [PATCH] Fix slot items to fully work with the stashing prefab flow --- Assets/Data/Settings/DefaultSettings.asset | 5 +- Assets/Prefabs/Items/Meat Variant.prefab | 107 ++++++++++++++++++ Assets/Prefabs/Items/Meat Variant.prefab.meta | 7 ++ Assets/Scripts/Core/GameSettings.cs | 2 +- .../Interactions/CombineWithBehavior.cs | 34 ++++-- .../Scripts/Interactions/SlotItemBehavior.cs | 74 ++++++++++-- Assets/Scripts/Movement/FollowerController.cs | 84 ++++++++++++-- 7 files changed, 278 insertions(+), 35 deletions(-) create mode 100644 Assets/Prefabs/Items/Meat Variant.prefab create mode 100644 Assets/Prefabs/Items/Meat Variant.prefab.meta diff --git a/Assets/Data/Settings/DefaultSettings.asset b/Assets/Data/Settings/DefaultSettings.asset index 8339ff01..58e6d735 100644 --- a/Assets/Data/Settings/DefaultSettings.asset +++ b/Assets/Data/Settings/DefaultSettings.asset @@ -39,10 +39,7 @@ MonoBehaviour: combinationRules: - itemA: {fileID: 11400000, guid: 33e7ca06b22108d4e802486e08bcdfd1, type: 2} itemB: {fileID: 11400000, guid: 8b2616beb14825a46b9b1ed85ad3cb25, type: 2} - result: {fileID: 11400000, guid: ecae2d83a5ab2a047a2733ebff607380, type: 2} - - itemA: {fileID: 11400000, guid: 983414276ae3f004c854e9c450f27f88, type: 2} - itemB: {fileID: 11400000, guid: 0c6986639ca176a419c92f5a327d95ce, type: 2} - result: {fileID: 11400000, guid: 6934dcb56c610c44da228f7f24ca13c9, type: 2} + resultPrefab: {fileID: 1610562450228973293, guid: 58654125374567147839eb382fcde422, type: 3} slotItemConfigs: - slotItem: {fileID: 11400000, guid: e0fad48a84a6b6346ac17c84bc512500, type: 2} allowedItems: diff --git a/Assets/Prefabs/Items/Meat Variant.prefab b/Assets/Prefabs/Items/Meat Variant.prefab new file mode 100644 index 00000000..6e879757 --- /dev/null +++ b/Assets/Prefabs/Items/Meat Variant.prefab @@ -0,0 +1,107 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &8143515500611931992 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 592045584872845087, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: itemData + value: + objectReference: {fileID: 11400000, guid: ecae2d83a5ab2a047a2733ebff607380, type: 2} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalPosition.x + value: -8.48395 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalPosition.y + value: -1.95773 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1730119453103664125, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.newSize.x + value: 0.69 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.newSize.y + value: 0.45 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.oldSize.x + value: 0.69 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.oldSize.y + value: 0.45 + objectReference: {fileID: 0} + - target: {fileID: 3070149615425714466, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_SpriteTilingProperty.adaptiveTilingThreshold + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 7447346505753002421, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_Name + value: Meat + objectReference: {fileID: 0} + - target: {fileID: 7494677664706785084, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_Size.x + value: 0.69 + objectReference: {fileID: 0} + - target: {fileID: 7494677664706785084, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_Size.y + value: 0.45 + objectReference: {fileID: 0} + - target: {fileID: 7494677664706785084, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_Sprite + value: + objectReference: {fileID: 6282751622250221668, guid: 204325ac88be74d4d882a078c64cf5e1, type: 3} + - target: {fileID: 7494677664706785084, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} + propertyPath: m_WasSpriteAssigned + value: 1 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3} diff --git a/Assets/Prefabs/Items/Meat Variant.prefab.meta b/Assets/Prefabs/Items/Meat Variant.prefab.meta new file mode 100644 index 00000000..87c8792b --- /dev/null +++ b/Assets/Prefabs/Items/Meat Variant.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 58654125374567147839eb382fcde422 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Core/GameSettings.cs b/Assets/Scripts/Core/GameSettings.cs index 8c921f14..6e33400e 100644 --- a/Assets/Scripts/Core/GameSettings.cs +++ b/Assets/Scripts/Core/GameSettings.cs @@ -56,7 +56,7 @@ public class GameSettings : ScriptableObject public class CombinationRule { public PickupItemData itemA; public PickupItemData itemB; - public PickupItemData result; + public GameObject resultPrefab; // The prefab to spawn as the result } [System.Serializable] diff --git a/Assets/Scripts/Interactions/CombineWithBehavior.cs b/Assets/Scripts/Interactions/CombineWithBehavior.cs index 84ac49bc..70638d52 100644 --- a/Assets/Scripts/Interactions/CombineWithBehavior.cs +++ b/Assets/Scripts/Interactions/CombineWithBehavior.cs @@ -28,19 +28,35 @@ public class CombineWithBehavior : InteractionRequirementBase return false; } var rule = GameManager.Instance.GetCombinationRule(heldItem, pickup.itemData); - if (rule != null && rule.result != null) + if (rule != null && rule.resultPrefab != null) { - // Remove both items and add result to follower's inventory - follower.SetHeldItem(rule.result); - follower.justCombined = true; - OnSuccess?.Invoke(); - return true; + // Instantiate the result prefab at the pickup's position + var resultObj = GameObject.Instantiate(rule.resultPrefab, pickup.transform.position, Quaternion.identity); + var resultPickup = resultObj.GetComponent(); + if (resultPickup != null) + { + // Hide and parent to follower + resultObj.SetActive(false); + resultObj.transform.SetParent(follower.transform); + // Set held item and icon from the spawned prefab + follower.SetHeldItem(resultPickup.itemData, resultPickup.iconRenderer); + // Cache the spawned object as the held item + follower.CacheHeldPickupObject(resultObj); + follower.justCombined = true; + OnSuccess?.Invoke(); + return true; + } + else + { + Debug.LogWarning("Result prefab does not have a Pickup component."); + GameObject.Destroy(resultObj); + OnFailure?.Invoke(); + return false; + } } else { - string heldName = heldItem.itemName ?? "an item"; - string targetName = pickup.itemData.itemName ?? "target item"; - // DebugUIMessage.Show($"Cannot combine {heldName} with {targetName}."); + // DebugUIMessage.Show($"Cannot combine {heldItem.itemName ?? \"an item\"} with {pickup.itemData.itemName ?? \"target item\"}."); OnFailure?.Invoke(); return true; } diff --git a/Assets/Scripts/Interactions/SlotItemBehavior.cs b/Assets/Scripts/Interactions/SlotItemBehavior.cs index afaf88ce..49714b97 100644 --- a/Assets/Scripts/Interactions/SlotItemBehavior.cs +++ b/Assets/Scripts/Interactions/SlotItemBehavior.cs @@ -19,6 +19,45 @@ public class SlotItemBehavior : InteractionRequirementBase /// public SpriteRenderer slottedItemRenderer; + private GameObject _cachedSlottedObject = null; + + // Helper for slotting an object, with option to skip destruction (for swap) + private void SetSlottedObject(GameObject obj) + { + _cachedSlottedObject = obj; + if (_cachedSlottedObject != null) + { + _cachedSlottedObject.SetActive(false); + } + } + + private void RemoveSlottedObject() + { + if (_cachedSlottedObject != null) + { + Destroy(_cachedSlottedObject); + _cachedSlottedObject = null; + } + } + + private void CacheSlottedObject(GameObject obj) + { + // Only destroy if not swapping + RemoveSlottedObject(); + SetSlottedObject(obj); + } + + private void RestoreSlottedObject(Vector3 position) + { + if (_cachedSlottedObject != null) + { + _cachedSlottedObject.transform.position = position; + _cachedSlottedObject.transform.SetParent(null); + _cachedSlottedObject.SetActive(true); + _cachedSlottedObject = null; + } + } + /// /// Attempts to interact with the slot, handling slotting, swapping, or picking up items. /// @@ -27,6 +66,7 @@ public class SlotItemBehavior : InteractionRequirementBase public override bool TryInteract(FollowerController follower) { var heldItem = follower.CurrentlyHeldItem; + var heldObj = follower.GetHeldPickupObject(); var pickup = GetComponent(); var slotItem = pickup != null ? pickup.itemData : null; var config = GameManager.Instance.GetSlotItemConfig(slotItem); @@ -34,33 +74,44 @@ public class SlotItemBehavior : InteractionRequirementBase var forbidden = config?.forbiddenItems ?? new List(); // CASE 1: No held item, slot has item -> pick up slotted item - if (heldItem == null && currentlySlottedItem != null) + if (heldItem == null && _cachedSlottedObject != null) { - follower.SetHeldItem(currentlySlottedItem); + follower.SetHeldItemFromObject(_cachedSlottedObject); + RemoveSlottedObject(); currentlySlottedItem = null; UpdateSlottedSprite(); return true; } // CASE 2: Held item, slot has item -> swap - if (heldItem != null && currentlySlottedItem != null) + if (heldItem != null && _cachedSlottedObject != null) { - var temp = currentlySlottedItem; - currentlySlottedItem = heldItem; + var followerHeldObj = heldObj; + var followerHeldItem = heldItem; + var slotObj = _cachedSlottedObject; + var slotItemData = currentlySlottedItem; + + // 1. Slot the follower's held object (do NOT destroy the old one) + SetSlottedObject(followerHeldObj); + currentlySlottedItem = followerHeldItem; UpdateSlottedSprite(); - follower.SetHeldItem(temp); + + // 2. Give the slot's object to the follower + follower.SetHeldItemFromObject(slotObj); + return true; } // CASE 3: Held item, slot empty -> slot the held item - if (heldItem != null && currentlySlottedItem == null) + if (heldItem != null && _cachedSlottedObject == null) { if (forbidden.Contains(heldItem)) { DebugUIMessage.Show("Can't place that here."); return false; } + CacheSlottedObject(heldObj); currentlySlottedItem = heldItem; UpdateSlottedSprite(); - follower.SetHeldItem(null); + follower.ClearHeldItem(); if (allowed.Contains(heldItem)) { OnSuccess?.Invoke(); @@ -74,7 +125,7 @@ public class SlotItemBehavior : InteractionRequirementBase } } // CASE 4: No held item, slot empty -> show warning - if (heldItem == null && currentlySlottedItem == null) + if (heldItem == null && _cachedSlottedObject == null) { DebugUIMessage.Show("This requires an item."); return false; @@ -110,4 +161,9 @@ public class SlotItemBehavior : InteractionRequirementBase slottedItemRenderer.sprite = null; } } + + void Update() + { + Debug.Log($"[SlotItemBehavior] _cachedSlottedObject: {_cachedSlottedObject} (GameObject name: {(_cachedSlottedObject != null ? _cachedSlottedObject.name : "null")})", this); + } } diff --git a/Assets/Scripts/Movement/FollowerController.cs b/Assets/Scripts/Movement/FollowerController.cs index 6d13d06e..a0ba55e7 100644 --- a/Assets/Scripts/Movement/FollowerController.cs +++ b/Assets/Scripts/Movement/FollowerController.cs @@ -60,11 +60,20 @@ public class FollowerController : MonoBehaviour /// Cache for the currently picked-up GameObject (hidden while held). /// private GameObject _cachedPickupObject = null; + public bool justCombined = false; /// - /// Set to true if the follower just combined items. + /// Caches the given pickup object as the currently held item, hides it, and parents it to the follower. /// - public bool justCombined = false; + public void CacheHeldPickupObject(GameObject obj) + { + // Do not destroy the previous object; just replace and hide + _cachedPickupObject = obj; + if (_cachedPickupObject != null) + { + _cachedPickupObject.SetActive(false); + } + } void Awake() { @@ -281,6 +290,45 @@ public class FollowerController : MonoBehaviour _lastInteractionSuccess = success; } + public GameObject GetHeldPickupObject() + { + return _cachedPickupObject; + } + + public void SetHeldItemFromObject(GameObject obj) + { + if (obj == null) + { + ClearHeldItem(); + return; + } + var pickup = obj.GetComponent(); + if (pickup != null) + { + SetHeldItem(pickup.itemData, pickup.iconRenderer); + CacheHeldPickupObject(obj); + } + else + { + ClearHeldItem(); + } + } + + public void ClearHeldItem() + { + if (_cachedPickupObject != null) + { + // Destroy(_cachedPickupObject); + _cachedPickupObject = null; + } + _currentlyHeldItem = null; + if (heldObjectRenderer != null) + { + heldObjectRenderer.sprite = null; + heldObjectRenderer.enabled = false; + } + } + private System.Collections.IEnumerator PickupSequence(Vector2 itemPosition, Transform playerTransform) { _isManualFollowing = false; @@ -314,12 +362,6 @@ public class FollowerController : MonoBehaviour } if (justCombined) { - // Combination: just destroy the pickup, don't spawn anything - if (_cachedPickupObject != null) - { - Destroy(_cachedPickupObject); - _cachedPickupObject = null; - } GameObject.Destroy(pickup.gameObject); justCombined = false; break; @@ -334,10 +376,7 @@ public class FollowerController : MonoBehaviour _cachedPickupObject = null; } SetHeldItem(pickup.itemData, pickup.iconRenderer); - // Cache and hide the picked up object - _cachedPickupObject = pickup.gameObject; - _cachedPickupObject.SetActive(false); - _cachedPickupObject.transform.SetParent(this.transform); + CacheHeldPickupObject(pickup.gameObject); break; } } @@ -367,6 +406,27 @@ public class FollowerController : MonoBehaviour _pickupCoroutine = null; } + /// + /// Drop the held item at the specified position, unparenting and activating it. + /// + /// The world position to drop the item at. + public void DropHeldItemAt(Vector3 position) + { + if (_cachedPickupObject != null) + { + _cachedPickupObject.transform.position = position; + _cachedPickupObject.transform.SetParent(null); + _cachedPickupObject.SetActive(true); + _cachedPickupObject = null; + _currentlyHeldItem = null; + if (heldObjectRenderer != null) + { + heldObjectRenderer.sprite = null; + heldObjectRenderer.enabled = false; + } + } + } + void OnDrawGizmos() { if (debugDrawTarget && Application.isPlaying)