working on quest and interaction system

This commit is contained in:
2026-10-01 15:14:37 +01:00
parent 163939dab8
commit e8af8950e1
1198 changed files with 2503496 additions and 93 deletions

View File

@@ -0,0 +1,172 @@
using UnityEngine;
/// <summary>
/// Place this on an NPC, Item, or Trigger Zone to easily connect it to the Quest System!
/// </summary>
public class QuestInteractable : MonoBehaviour, IInteractable
{
[Header("Quest Progression")]
[Tooltip("What action does this object trigger?")]
public ObjectiveType objectiveType;
[Tooltip("The unique ID that matches the QuestObjective (e.g., 'wolf', 'ancient_relic')")]
public string targetID;
[Tooltip("How much to progress the objective (usually 1)")]
public int progressAmount = 1;
[Header("Interaction Settings")]
[Tooltip("Can the player walk up and press E on this object?")]
public bool isInteractable = false;
[Tooltip("If true, the player can ONLY interact with this if they currently have the matching quest objective active!")]
public bool requireActiveObjective = false;
[Tooltip("The action verb for the prompt (e.g., 'Open', 'Collect', 'Steal')")]
public string interactAction = "Interact";
[Tooltip("The secondary text to display (e.g. the name of the item or 'Press E')")]
public string interactTargetName = "[ E ]";
[Header("Destination/Trigger Settings")]
[Tooltip("Check this if walking into a Trigger Collider should automatically complete the objective.")]
public bool completeOnTriggerEnter = false;
[Tooltip("A visual marker GameObject to enable while active, and disable when completed.")]
public GameObject destinationMarker;
[Tooltip("A TMPro text element to show the distance counting down as the player gets closer.")]
public TMPro.TextMeshProUGUI distanceText;
[Header("Dynamic Marker Height")]
[Tooltip("If true, the marker will physically raise into the sky when far away, and lower to the ground when close.")]
public bool scaleMarkerHeight = true;
public float maxDistanceForHeight = 50f;
public float minHeight = 1.5f;
public float maxHeight = 15f;
[Tooltip("How far from the center the marker should hit its lowest point. If you have a large Box Collider, set this to half its width (e.g., 3m) so the marker bottoms out right as you touch the edge!")]
public float arrivalDistanceOffset = 3f;
[Header("Item Settings")]
[Tooltip("Check this if this is an item that should destroy itself when collected/triggered")]
public bool destroyOnTrigger;
private bool _hasBeenTriggered = false;
private Transform _playerTransform;
private void Start()
{
// Find player once at start for distance tracking
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
if (playerObj != null)
{
_playerTransform = playerObj.transform;
}
}
private void Update()
{
// Update distance countdown if marker is active and we haven't triggered
if (completeOnTriggerEnter && !_hasBeenTriggered && destinationMarker != null && destinationMarker.activeInHierarchy)
{
// Fallback: If the 'Player' tag is on a stationary object or missing, track the Main Camera!
// The Main Camera is guaranteed to move with you, so the distance will always update correctly.
if (_playerTransform == null || _playerTransform.position == Vector3.zero)
{
if (Camera.main != null) _playerTransform = Camera.main.transform;
}
if (_playerTransform != null)
{
float dist = Vector3.Distance(_playerTransform.position, transform.position);
if (distanceText != null)
{
distanceText.text = $"{Mathf.RoundToInt(dist)}m";
}
if (scaleMarkerHeight)
{
// Subtract the offset so it bottoms out at the edge of the collider instead of the exact center
float effectiveDist = Mathf.Max(0, dist - arrivalDistanceOffset);
// Calculate a 0 to 1 value (0 = at the edge, 1 = max distance away)
float t = Mathf.Clamp01(effectiveDist / maxDistanceForHeight);
// Lerp between our min and max height clamps
float targetY = Mathf.Lerp(minHeight, maxHeight, t);
Vector3 localPos = destinationMarker.transform.localPosition;
localPos.y = targetY;
destinationMarker.transform.localPosition = localPos;
}
}
}
}
private void OnTriggerEnter(Collider other)
{
if (!completeOnTriggerEnter || _hasBeenTriggered) return;
// Ensure it's the player that walked into the trigger
if (other.CompareTag("Player") || other.transform.root.CompareTag("Player") || other.GetComponentInParent<PlayerStats>() != null)
{
TriggerQuestProgress();
}
}
// --- IInteractable Implementation ---
public string GetInteractText()
{
return interactAction;
}
public string GetSecondaryInteractText()
{
return interactTargetName;
}
public bool CanInteract()
{
if (!isInteractable || _hasBeenTriggered) return false;
// If this option is checked, we ask the QuestManager if this is our current objective!
if (requireActiveObjective)
{
if (QuestManager.Instance == null) return false;
return QuestManager.Instance.IsObjectiveActive(objectiveType, targetID);
}
return true;
}
public void Interact(GameObject player)
{
TriggerQuestProgress();
}
// ------------------------------------
/// <summary>
/// Call this from your existing interaction scripts, or a Unity Event when picking up an item!
/// </summary>
public void TriggerQuestProgress()
{
if (_hasBeenTriggered) return;
bool wasSuccessful = false;
if (QuestManager.Instance != null)
{
wasSuccessful = QuestManager.Instance.ProgressObjective(objectiveType, targetID, progressAmount);
}
// If the objective wasn't active (or was wrong type), we ignore it so they can try again later
if (!wasSuccessful) return;
_hasBeenTriggered = true;
if (destinationMarker != null)
{
destinationMarker.SetActive(false);
}
if (destroyOnTrigger)
{
Destroy(gameObject);
}
}
}