Working on quests, moved to CineMachine for camera and started to build out areas, started combat system

This commit is contained in:
2026-02-12 17:30:04 +00:00
parent c08d7d8830
commit d564c5f44e
50 changed files with 21700 additions and 10726 deletions

View File

@@ -1,6 +1,8 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.AI;
using EasyTalk.Controller;
using EasyTalk.Nodes;
public abstract class Character : MonoBehaviour
{
@@ -34,6 +36,22 @@ public abstract class Character : MonoBehaviour
[SerializeField]
protected int MaxHp = 10;
[Header("Dialogue")]
public bool hasDialogue = false;
[Tooltip("The Dialogue asset for this character. Will be loaded into the central DialogueController.")]
public Dialogue dialogue;
[Tooltip("Optional: Entry point name for starting dialogue. Leave empty to use default entry point.")]
public string dialogueEntryPoint = "";
[Tooltip("Name of the GameObject with the central DialogueController. Leave empty to auto-find.")]
public string dialogueManagerName = "Manager";
[Header("Debug")]
[SerializeField]
protected bool enableDebugLogs = false;
// Cache the central DialogueController
protected static DialogueController centralDialogueController;
[Header("Components")]
// Optional movement controller reference, assign in inspector if used
public NavMeshMovementController MovementController;
@@ -219,6 +237,174 @@ public abstract class Character : MonoBehaviour
SetFloatIfExists(paramHeadVertical, vertical);
}
/// <summary>
/// Make character look at a target with head rotation, body rotation when clamped
/// </summary>
public void LookAtTarget(Transform target)
{
if (target == null) return;
Vector3 directionToTarget = target.position - transform.position;
directionToTarget.y = 0; // Keep on horizontal plane
if (directionToTarget.sqrMagnitude < 0.01f) return;
// Calculate angle to target
float angleToTarget = Vector3.SignedAngle(transform.forward, directionToTarget, Vector3.up);
// Head rotation limits (before body needs to rotate)
const float headHorizontalLimit = 60f;
if (Mathf.Abs(angleToTarget) > headHorizontalLimit)
{
// Rotate body to face target
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f);
// Reset head to neutral when body rotates
SetHeadLook(0f, 0f);
}
else
{
// Just rotate head
float normalizedAngle = angleToTarget / headHorizontalLimit; // -1 to 1
SetHeadLook(normalizedAngle, 0f);
}
}
/// <summary>
/// Called when player interacts with this character. Override to add custom behavior.
/// </summary>
public virtual void OnInteract(GameObject player)
{
Log($"Interacting with {gameObject.name}");
// Look at player
if (player != null)
{
LookAtTarget(player.transform);
// Make player look at this character
Character playerChar = player.GetComponent<Character>();
if (playerChar != null)
{
playerChar.LookAtTarget(transform);
}
}
if (hasDialogue)
{
ShowDialogue();
}
}
/// <summary>
/// Display dialogue for this character using EasyTalk with centralized DialogueController
/// </summary>
/// <param name="entryPoint">Optional entry point override. If null/empty, uses dialogueEntryPoint field.</param>
protected virtual void ShowDialogue(string entryPoint = null)
{
// Find the central DialogueController if not cached
if (centralDialogueController == null)
{
centralDialogueController = FindCentralDialogueController();
}
// Check if we have both a controller and dialogue asset
if (centralDialogueController != null && dialogue != null)
{
// Load this character's dialogue into the central controller
centralDialogueController.ChangeDialogue(dialogue);
// Use provided entry point, fallback to field, then default
string targetEntryPoint = !string.IsNullOrEmpty(entryPoint) ? entryPoint : dialogueEntryPoint;
// Play dialogue with optional entry point
if (!string.IsNullOrEmpty(targetEntryPoint))
{
centralDialogueController.PlayDialogue(targetEntryPoint);
Log($"{gameObject.name}: Starting EasyTalk dialogue at entry point '{targetEntryPoint}'");
}
else
{
centralDialogueController.PlayDialogue();
Log($"{gameObject.name}: Starting EasyTalk dialogue");
}
}
else
{
// Warnings for missing components
if (centralDialogueController == null)
{
Debug.LogWarning($"No central DialogueController found. Make sure there's a DialogueController on a GameObject named '{dialogueManagerName}'.", gameObject);
}
if (dialogue == null)
{
Debug.LogWarning($"No Dialogue asset assigned to {gameObject.name}. Assign a Dialogue asset in the inspector.", gameObject);
}
}
}
/// <summary>
/// Public wrapper for UnityEvents or other triggers
/// </summary>
/// <param name="entryPoint">Optional entry point name. If empty, uses default dialogueEntryPoint.</param>
public virtual void StartDialogue(string entryPoint = "")
{
// Look at player if available
if (Player.current != null)
{
LookAtTarget(Player.current.transform);
// Make player look at this character
Player.current.LookAtTarget(transform);
}
ShowDialogue(entryPoint);
}
/// <summary>
/// Finds the central DialogueController in the scene
/// </summary>
protected DialogueController FindCentralDialogueController()
{
// First, try to find by the specified manager name
if (!string.IsNullOrEmpty(dialogueManagerName))
{
GameObject manager = GameObject.Find(dialogueManagerName);
if (manager != null)
{
DialogueController controller = manager.GetComponent<DialogueController>();
if (controller != null)
{
Log($"Found central DialogueController on '{dialogueManagerName}'");
return controller;
}
}
}
// Fallback: search for any DialogueController in the scene
DialogueController foundController = FindAnyObjectByType<DialogueController>();
if (foundController != null)
{
Log($"Found DialogueController via FindAnyObjectByType on '{foundController.gameObject.name}'");
return foundController;
}
return null;
}
/// <summary>
/// Debug logging utility
/// </summary>
protected void Log(string message)
{
if (enableDebugLogs)
{
Debug.Log($"[{GetType().Name}] {message}", gameObject);
}
}
private void ApplyStaticAnimatorParams()
{
if (animator == null) return;
@@ -322,6 +508,9 @@ public abstract class Character : MonoBehaviour
yield return new WaitForSeconds(attackHoldSeconds);
}
SetBoolIfExists(paramShoot, false);
// Wait one frame for animator to process the Shoot_b = false transition
yield return null;
if (restoreWeaponAfterAttack)
{