Big script cleanup. Remove the examples from Ropes' external package
This commit is contained in:
48
Assets/Scripts/Interactions/CombineWithBehavior.cs
Normal file
48
Assets/Scripts/Interactions/CombineWithBehavior.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction requirement that allows combining the follower's held item with this pickup if a valid combination rule exists.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Pickup))]
|
||||
public class CombineWithBehavior : InteractionRequirementBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to combine the follower's held item with this pickup's item.
|
||||
/// </summary>
|
||||
/// <param name="follower">The follower attempting the interaction.</param>
|
||||
/// <returns>True if the combination was successful, false otherwise.</returns>
|
||||
public override bool TryInteract(FollowerController follower)
|
||||
{
|
||||
var heldItem = follower.currentlyHeldItem;
|
||||
var pickup = GetComponent<Pickup>();
|
||||
if (heldItem == null)
|
||||
{
|
||||
DebugUIMessage.Show("You need an item to combine.");
|
||||
OnFailure?.Invoke();
|
||||
return false;
|
||||
}
|
||||
if (pickup == null || pickup.itemData == null)
|
||||
{
|
||||
DebugUIMessage.Show("Target item is missing or invalid.");
|
||||
OnFailure?.Invoke();
|
||||
return false;
|
||||
}
|
||||
var rule = GameManager.Instance.GetCombinationRule(heldItem, pickup.itemData);
|
||||
if (rule != null && rule.result != null)
|
||||
{
|
||||
// Remove both items and add result to follower's inventory
|
||||
follower.SetHeldItem(rule.result);
|
||||
follower.justCombined = true;
|
||||
OnSuccess?.Invoke();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
string heldName = heldItem.itemName ?? "an item";
|
||||
string targetName = pickup.itemData.itemName ?? "target item";
|
||||
DebugUIMessage.Show($"Cannot combine {heldName} with {targetName}.");
|
||||
OnFailure?.Invoke();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Interactions/CombineWithBehavior.cs.meta
Normal file
3
Assets/Scripts/Interactions/CombineWithBehavior.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21401a3b30134380bb205964d9e5c67d
|
||||
timeCreated: 1756981777
|
||||
71
Assets/Scripts/Interactions/Interactable.cs
Normal file
71
Assets/Scripts/Interactions/Interactable.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an interactable object that can respond to tap input events.
|
||||
/// </summary>
|
||||
public class Interactable : MonoBehaviour, ITouchInputConsumer
|
||||
{
|
||||
public event Action StartedInteraction;
|
||||
public event Action<bool> InteractionComplete;
|
||||
|
||||
private ObjectiveStepBehaviour stepBehaviour;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
stepBehaviour = GetComponent<ObjectiveStepBehaviour>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles tap input. Triggers interaction logic.
|
||||
/// </summary>
|
||||
public void OnTap(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[Interactable] OnTap at {worldPosition} on {gameObject.name}");
|
||||
StartedInteraction?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No hold behavior for interactables.
|
||||
/// </summary>
|
||||
public void OnHoldStart(Vector2 worldPosition) { }
|
||||
public void OnHoldMove(Vector2 worldPosition) { }
|
||||
public void OnHoldEnd(Vector2 worldPosition) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the follower arrives at this interactable.
|
||||
/// </summary>
|
||||
public bool OnFollowerArrived(FollowerController follower)
|
||||
{
|
||||
// Check if step is locked here
|
||||
if (stepBehaviour != null && !stepBehaviour.IsStepUnlocked())
|
||||
{
|
||||
DebugUIMessage.Show("Item is not unlocked yet");
|
||||
Debug.Log("[Puzzles] Tried to interact with locked step: " + gameObject.name);
|
||||
InteractionComplete?.Invoke(false);
|
||||
return false;
|
||||
}
|
||||
var requirements = GetComponents<InteractionRequirementBase>();
|
||||
if (requirements.Length == 0)
|
||||
{
|
||||
InteractionComplete?.Invoke(true);
|
||||
return true;
|
||||
}
|
||||
bool anySuccess = false;
|
||||
foreach (var req in requirements)
|
||||
{
|
||||
if (req.TryInteract(follower))
|
||||
{
|
||||
anySuccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
InteractionComplete?.Invoke(anySuccess);
|
||||
if (!anySuccess)
|
||||
{
|
||||
Debug.Log($"[Interactable] No interaction requirements succeeded for {gameObject.name}");
|
||||
// Optionally trigger a default failure event or feedback here
|
||||
}
|
||||
return anySuccess;
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Interactions/Interactable.cs.meta
Normal file
3
Assets/Scripts/Interactions/Interactable.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73d6494a73174ffabc6a7d3089d51e73
|
||||
timeCreated: 1756733750
|
||||
19
Assets/Scripts/Interactions/InteractionRequirementBase.cs
Normal file
19
Assets/Scripts/Interactions/InteractionRequirementBase.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base class for interaction requirements. Defines success/failure events and the TryInteract contract.
|
||||
/// </summary>
|
||||
public abstract class InteractionRequirementBase : MonoBehaviour
|
||||
{
|
||||
[Header("Events")]
|
||||
public UnityEvent OnSuccess;
|
||||
public UnityEvent OnFailure;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to perform the interaction requirement with the given follower.
|
||||
/// </summary>
|
||||
/// <param name="follower">The follower attempting the interaction.</param>
|
||||
/// <returns>True if the interaction was successful, false otherwise.</returns>
|
||||
public abstract bool TryInteract(FollowerController follower);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2b7dc7a0fd74f3387095faa9f9dbb32
|
||||
timeCreated: 1756981728
|
||||
171
Assets/Scripts/Interactions/Pickup.cs
Normal file
171
Assets/Scripts/Interactions/Pickup.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Input;
|
||||
using UnityEngine;
|
||||
|
||||
public class Pickup : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Data for the pickup item (icon, name, etc).
|
||||
/// </summary>
|
||||
public PickupItemData itemData;
|
||||
/// <summary>
|
||||
/// Renderer for the pickup icon.
|
||||
/// </summary>
|
||||
public SpriteRenderer iconRenderer;
|
||||
private Interactable interactable;
|
||||
|
||||
private bool pickupInProgress = false;
|
||||
|
||||
/// <summary>
|
||||
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
||||
/// </summary>
|
||||
void Awake()
|
||||
{
|
||||
if (iconRenderer == null)
|
||||
iconRenderer = GetComponent<SpriteRenderer>();
|
||||
interactable = GetComponent<Interactable>();
|
||||
if (interactable != null)
|
||||
{
|
||||
interactable.StartedInteraction += OnStartedInteraction;
|
||||
interactable.InteractionComplete += OnInteractionComplete;
|
||||
}
|
||||
ApplyItemData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity OnDestroy callback. Cleans up event handlers.
|
||||
/// </summary>
|
||||
void OnDestroy()
|
||||
{
|
||||
if (interactable != null)
|
||||
{
|
||||
interactable.StartedInteraction -= OnStartedInteraction;
|
||||
interactable.InteractionComplete -= OnInteractionComplete;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Unity OnValidate callback. Ensures icon and data are up to date in editor.
|
||||
/// </summary>
|
||||
void OnValidate()
|
||||
{
|
||||
if (iconRenderer == null)
|
||||
iconRenderer = GetComponent<SpriteRenderer>();
|
||||
ApplyItemData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws gizmos for pickup interaction range in the editor.
|
||||
/// </summary>
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
float playerStopDistance = GameManager.Instance.PlayerStopDistance;
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, playerStopDistance);
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
if (playerObj != null)
|
||||
{
|
||||
Vector3 stopPoint = transform.position + (playerObj.transform.position - transform.position).normalized * playerStopDistance;
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawSphere(stopPoint, 0.15f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Applies the item data to the pickup (icon, name, etc).
|
||||
/// </summary>
|
||||
public void ApplyItemData()
|
||||
{
|
||||
if (itemData != null)
|
||||
{
|
||||
if (iconRenderer != null && itemData.mapSprite != null)
|
||||
{
|
||||
iconRenderer.sprite = itemData.mapSprite;
|
||||
}
|
||||
gameObject.name = itemData.itemName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the start of an interaction (player approaches, then follower picks up).
|
||||
/// </summary>
|
||||
private void OnStartedInteraction()
|
||||
{
|
||||
if (pickupInProgress) return;
|
||||
var playerObj = GameObject.FindGameObjectWithTag("Player");
|
||||
var followerObj = GameObject.FindGameObjectWithTag("Pulver");
|
||||
if (playerObj == null || followerObj == null)
|
||||
{
|
||||
Debug.LogWarning("Pickup: Player or Follower not found.");
|
||||
return;
|
||||
}
|
||||
var playerController = playerObj.GetComponent<PlayerTouchController>();
|
||||
var followerController = followerObj.GetComponent<FollowerController>();
|
||||
if (playerController == null || followerController == null)
|
||||
{
|
||||
Debug.LogWarning("Pickup: PlayerTouchController or FollowerController missing.");
|
||||
return;
|
||||
}
|
||||
float playerStopDistance = GameManager.Instance.PlayerStopDistance;
|
||||
float followerPickupDelay = GameManager.Instance.FollowerPickupDelay;
|
||||
// --- Local event/coroutine handlers ---
|
||||
void OnPlayerArrived()
|
||||
{
|
||||
playerController.OnArrivedAtTarget -= OnPlayerArrived;
|
||||
playerController.OnMoveToCancelled -= OnPlayerMoveCancelled;
|
||||
pickupInProgress = true;
|
||||
StartCoroutine(DispatchFollower());
|
||||
}
|
||||
void OnPlayerMoveCancelled()
|
||||
{
|
||||
playerController.OnArrivedAtTarget -= OnPlayerArrived;
|
||||
playerController.OnMoveToCancelled -= OnPlayerMoveCancelled;
|
||||
pickupInProgress = false;
|
||||
}
|
||||
System.Collections.IEnumerator DispatchFollower()
|
||||
{
|
||||
yield return new WaitForSeconds(followerPickupDelay);
|
||||
followerController.OnPickupArrived += OnFollowerArrived;
|
||||
followerController.OnPickupReturned += OnFollowerReturned;
|
||||
followerController.GoToPointAndReturn(transform.position, playerObj.transform);
|
||||
}
|
||||
void OnFollowerArrived()
|
||||
{
|
||||
followerController.OnPickupArrived -= OnFollowerArrived;
|
||||
bool interactionSuccess = true;
|
||||
if (interactable != null)
|
||||
{
|
||||
interactionSuccess = interactable.OnFollowerArrived(followerController);
|
||||
}
|
||||
followerController.SetInteractionResult(interactionSuccess);
|
||||
}
|
||||
void OnFollowerReturned()
|
||||
{
|
||||
followerController.OnPickupReturned -= OnFollowerReturned;
|
||||
pickupInProgress = false;
|
||||
}
|
||||
playerController.OnArrivedAtTarget += OnPlayerArrived;
|
||||
playerController.OnMoveToCancelled += OnPlayerMoveCancelled;
|
||||
Vector3 stopPoint = transform.position + (playerObj.transform.position - transform.position).normalized * playerStopDistance;
|
||||
float dist = Vector2.Distance(new Vector2(playerObj.transform.position.x, playerObj.transform.position.y), new Vector2(stopPoint.x, stopPoint.y));
|
||||
if (dist <= 0.2f)
|
||||
{
|
||||
OnPlayerArrived();
|
||||
}
|
||||
else
|
||||
{
|
||||
playerController.MoveToAndNotify(stopPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles completion of the interaction (e.g., after pickup is done).
|
||||
/// </summary>
|
||||
/// <param name="success">Whether the interaction was successful.</param>
|
||||
private void OnInteractionComplete(bool success)
|
||||
{
|
||||
if (!success) return;
|
||||
// Optionally, add logic to disable the pickup or provide feedback
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Interactions/Pickup.cs.meta
Normal file
3
Assets/Scripts/Interactions/Pickup.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7846448751da4bdbaaa5cb87890dca42
|
||||
timeCreated: 1756733766
|
||||
10
Assets/Scripts/Interactions/PickupItemData.cs
Normal file
10
Assets/Scripts/Interactions/PickupItemData.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "PickupItemData", menuName = "Game/Pickup Item Data")]
|
||||
public class PickupItemData : ScriptableObject
|
||||
{
|
||||
public string itemName;
|
||||
[TextArea]
|
||||
public string description;
|
||||
public Sprite mapSprite;
|
||||
}
|
||||
3
Assets/Scripts/Interactions/PickupItemData.cs.meta
Normal file
3
Assets/Scripts/Interactions/PickupItemData.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 951b5c8af5114086a865d4bb7eae4548
|
||||
timeCreated: 1756733686
|
||||
41
Assets/Scripts/Interactions/RequiresItemBehavior.cs
Normal file
41
Assets/Scripts/Interactions/RequiresItemBehavior.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction requirement that checks if the follower is holding a specific required item.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Interactable))]
|
||||
public class RequiresItemBehavior : InteractionRequirementBase
|
||||
{
|
||||
[Header("Required Item")]
|
||||
public PickupItemData requiredItem;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to interact, succeeds only if the follower is holding the required item.
|
||||
/// </summary>
|
||||
/// <param name="follower">The follower attempting the interaction.</param>
|
||||
/// <returns>True if the interaction was successful, false otherwise.</returns>
|
||||
public override bool TryInteract(FollowerController follower)
|
||||
{
|
||||
var heldItem = follower.currentlyHeldItem;
|
||||
if (heldItem == requiredItem)
|
||||
{
|
||||
OnSuccess?.Invoke();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
string requiredName = requiredItem != null ? requiredItem.itemName : "required item";
|
||||
if (heldItem == null)
|
||||
{
|
||||
DebugUIMessage.Show($"You need {requiredName} to interact.");
|
||||
}
|
||||
else
|
||||
{
|
||||
string heldName = heldItem.itemName ?? "an item";
|
||||
DebugUIMessage.Show($"You need {requiredName}, but you are holding {heldName}.");
|
||||
}
|
||||
OnFailure?.Invoke();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Interactions/RequiresItemBehavior.cs.meta
Normal file
3
Assets/Scripts/Interactions/RequiresItemBehavior.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31103d67032c44a9b95ec014babe2c62
|
||||
timeCreated: 1756981777
|
||||
113
Assets/Scripts/Interactions/SlotItemBehavior.cs
Normal file
113
Assets/Scripts/Interactions/SlotItemBehavior.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction requirement that allows slotting, swapping, or picking up items in a slot.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Interactable))]
|
||||
[RequireComponent(typeof(Pickup))]
|
||||
public class SlotItemBehavior : InteractionRequirementBase
|
||||
{
|
||||
[Header("Slot State")]
|
||||
/// <summary>
|
||||
/// The item currently slotted in this slot.
|
||||
/// </summary>
|
||||
public PickupItemData currentlySlottedItem;
|
||||
/// <summary>
|
||||
/// The renderer for the slotted item's sprite.
|
||||
/// </summary>
|
||||
public SpriteRenderer slottedItemRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to interact with the slot, handling slotting, swapping, or picking up items.
|
||||
/// </summary>
|
||||
/// <param name="follower">The follower attempting the interaction.</param>
|
||||
/// <returns>True if the interaction was successful, false otherwise.</returns>
|
||||
public override bool TryInteract(FollowerController follower)
|
||||
{
|
||||
var heldItem = follower.currentlyHeldItem;
|
||||
var pickup = GetComponent<Pickup>();
|
||||
var slotItem = pickup != null ? pickup.itemData : null;
|
||||
var config = GameManager.Instance.GetSlotItemConfig(slotItem);
|
||||
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||
var forbidden = config?.forbiddenItems ?? new List<PickupItemData>();
|
||||
|
||||
// CASE 1: No held item, slot has item -> pick up slotted item
|
||||
if (heldItem == null && currentlySlottedItem != null)
|
||||
{
|
||||
follower.SetHeldItem(currentlySlottedItem);
|
||||
currentlySlottedItem = null;
|
||||
UpdateSlottedSprite();
|
||||
return true;
|
||||
}
|
||||
// CASE 2: Held item, slot has item -> swap
|
||||
if (heldItem != null && currentlySlottedItem != null)
|
||||
{
|
||||
var temp = currentlySlottedItem;
|
||||
currentlySlottedItem = heldItem;
|
||||
UpdateSlottedSprite();
|
||||
follower.SetHeldItem(temp);
|
||||
return true;
|
||||
}
|
||||
// CASE 3: Held item, slot empty -> slot the held item
|
||||
if (heldItem != null && currentlySlottedItem == null)
|
||||
{
|
||||
if (forbidden.Contains(heldItem))
|
||||
{
|
||||
DebugUIMessage.Show("Can't place that here.");
|
||||
return false;
|
||||
}
|
||||
currentlySlottedItem = heldItem;
|
||||
UpdateSlottedSprite();
|
||||
follower.SetHeldItem(null);
|
||||
if (allowed.Contains(heldItem))
|
||||
{
|
||||
OnSuccess?.Invoke();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugUIMessage.Show("I'm not sure this works.");
|
||||
OnFailure?.Invoke();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// CASE 4: No held item, slot empty -> show warning
|
||||
if (heldItem == null && currentlySlottedItem == null)
|
||||
{
|
||||
DebugUIMessage.Show("This requires an item.");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the sprite and scale for the currently slotted item.
|
||||
/// </summary>
|
||||
private void UpdateSlottedSprite()
|
||||
{
|
||||
if (slottedItemRenderer != null && currentlySlottedItem != null && currentlySlottedItem.mapSprite != null)
|
||||
{
|
||||
slottedItemRenderer.sprite = currentlySlottedItem.mapSprite;
|
||||
// Scale sprite to desired height, preserve aspect ratio, compensate for parent scale
|
||||
float desiredHeight = GameManager.Instance.HeldIconDisplayHeight;
|
||||
var sprite = currentlySlottedItem.mapSprite;
|
||||
float spriteHeight = sprite.bounds.size.y;
|
||||
float spriteWidth = sprite.bounds.size.x;
|
||||
Vector3 parentScale = slottedItemRenderer.transform.parent != null
|
||||
? slottedItemRenderer.transform.parent.localScale
|
||||
: Vector3.one;
|
||||
if (spriteHeight > 0f)
|
||||
{
|
||||
float uniformScale = desiredHeight / spriteHeight;
|
||||
float scale = uniformScale / Mathf.Max(parentScale.x, parentScale.y);
|
||||
slottedItemRenderer.transform.localScale = new Vector3(scale, scale, 1f);
|
||||
}
|
||||
}
|
||||
else if (slottedItemRenderer != null)
|
||||
{
|
||||
slottedItemRenderer.sprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Interactions/SlotItemBehavior.cs.meta
Normal file
3
Assets/Scripts/Interactions/SlotItemBehavior.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec1a2e6e32f746c4990c579e13b79104
|
||||
timeCreated: 1756985692
|
||||
Reference in New Issue
Block a user