Big script cleanup. Remove the examples from Ropes' external package

This commit is contained in:
Michal Pikulski
2025-09-06 21:01:54 +02:00
parent 045bd7966e
commit d3c6b838b4
134 changed files with 719 additions and 26298 deletions

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21401a3b30134380bb205964d9e5c67d
timeCreated: 1756981777

View 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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73d6494a73174ffabc6a7d3089d51e73
timeCreated: 1756733750

View 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);
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2b7dc7a0fd74f3387095faa9f9dbb32
timeCreated: 1756981728

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7846448751da4bdbaaa5cb87890dca42
timeCreated: 1756733766

View 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;
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 951b5c8af5114086a865d4bb7eae4548
timeCreated: 1756733686

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31103d67032c44a9b95ec014babe2c62
timeCreated: 1756981777

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec1a2e6e32f746c4990c579e13b79104
timeCreated: 1756985692