IndieGame/client/Assets/ParadoxNotion/NodeCanvas/Modules/DialogueTrees/DialogueTree.cs

299 lines
12 KiB
C#
Raw Normal View History

2024-10-11 10:12:15 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using NodeCanvas.Framework;
using ParadoxNotion;
using UnityEngine;
using Logger = ParadoxNotion.Services.Logger;
namespace NodeCanvas.DialogueTrees
{
///<summary> Use DialogueTrees to create Dialogues between Actors</summary>
[GraphInfo(
packageName = "NodeCanvas",
docsURL = "https://nodecanvas.paradoxnotion.com/documentation/",
resourcesURL = "https://nodecanvas.paradoxnotion.com/downloads/",
forumsURL = "https://nodecanvas.paradoxnotion.com/forums-page/"
)]
[CreateAssetMenu(menuName = "ParadoxNotion/NodeCanvas/Dialogue Tree Asset")]
public class DialogueTree : Graph
{
///----------------------------------------------------------------------------------------------
[System.Serializable]
class DerivedSerializationData
{
public List<ActorParameter> actorParameters;
}
public override object OnDerivedDataSerialization() {
var data = new DerivedSerializationData();
data.actorParameters = this.actorParameters;
return data;
}
public override void OnDerivedDataDeserialization(object data) {
if ( data is DerivedSerializationData ) {
this.actorParameters = ( (DerivedSerializationData)data ).actorParameters;
}
}
///----------------------------------------------------------------------------------------------
///<summary>An Actor Parameter</summary>
[System.Serializable]
public class ActorParameter
{
[SerializeField] private string _keyName;
[SerializeField] private string _id;
[SerializeField] private UnityEngine.Object _actorObject;
[System.NonSerialized] private IDialogueActor _actor;
///<summary>Key name of the parameter</summary>
public string name {
get { return _keyName; }
set { _keyName = value; }
}
///<summary>ID of the parameter</summary>
public string ID => string.IsNullOrEmpty(_id) ? _id = System.Guid.NewGuid().ToString() : _id;
///<summary>The reference actor of the parameter</summary>
public IDialogueActor actor {
get
{
if ( _actor == null ) {
_actor = _actorObject as IDialogueActor;
}
return _actor;
}
set
{
_actor = value;
_actorObject = value as UnityEngine.Object;
}
}
public ActorParameter() { }
public ActorParameter(string name) { this.name = name; }
public ActorParameter(string name, IDialogueActor actor) {
this.name = name;
this.actor = actor;
}
public override string ToString() { return name; }
}
///----------------------------------------------------------------------------------------------
///<summary>The string used for the starting actor"</summary>
public const string INSTIGATOR_NAME = "SELF";
///<summary>The dialogue actor parameters. We let Unity serialize this as well</summary>
[SerializeField] public List<ActorParameter> actorParameters = new List<ActorParameter>();
private bool enterStartNodeFlag;
public static event Action<DialogueTree> OnDialogueStarted;
public static event Action<DialogueTree> OnDialoguePaused;
public static event Action<DialogueTree> OnDialogueFinished;
public static event Action<SubtitlesRequestInfo> OnSubtitlesRequest;
public static event Action<MultipleChoiceRequestInfo> OnMultipleChoiceRequest;
///<summary>The current DialogueTree running</summary>
public static DialogueTree currentDialogue { get; private set; }
///<summary>The previous DialogueTree running</summary>
public static DialogueTree previousDialogue { get; private set; }
///<summary>The current node of this DialogueTree</summary>
public DTNode currentNode { get; private set; }
///----------------------------------------------------------------------------------------------
public override System.Type baseNodeType => typeof(DTNode);
public override bool requiresAgent => false;
public override bool requiresPrimeNode => true;
public override bool isTree => true;
public override bool allowBlackboardOverrides => true;
sealed public override bool canAcceptVariableDrops => false;
public sealed override PlanarDirection flowDirection => PlanarDirection.Vertical;
///----------------------------------------------------------------------------------------------
///<summary>A list of the defined names for the involved actor parameters</summary>
public List<string> definedActorParameterNames {
get
{
var list = actorParameters.Select(r => r.name).ToList();
list.Insert(0, INSTIGATOR_NAME);
return list;
}
}
///<summary>Returns the ActorParameter by id</summary>
public ActorParameter GetParameterByID(string id) {
return actorParameters.Find(p => p.ID == id);
}
///<summary>Returns the ActorParameter by name</summary>
public ActorParameter GetParameterByName(string paramName) {
return actorParameters.Find(p => p.name == paramName);
}
///<summary>Returns the actor by parameter id.</summary>
public IDialogueActor GetActorReferenceByID(string id) {
var param = GetParameterByID(id);
return param != null ? GetActorReferenceByName(param.name) : null;
}
///<summary>Resolves and gets an actor based on the key name</summary>
public IDialogueActor GetActorReferenceByName(string paramName) {
//Check for INSTIGATOR selection
if ( paramName == INSTIGATOR_NAME ) {
//return it directly if it implements IDialogueActor
if ( agent is IDialogueActor ) {
return (IDialogueActor)agent;
}
//Otherwise use the default actor and set name and transform from agent
if ( agent != null ) {
return new ProxyDialogueActor(agent.gameObject.name, agent.transform);
}
return new ProxyDialogueActor("NO ACTOR", null);
}
//Check for non INSTIGATOR selection. If there IS an actor reference return it
var refData = actorParameters.Find(r => r.name == paramName);
if ( refData != null && refData.actor != null ) {
return refData.actor;
}
//Otherwise use the default actor and set the name to the key and null transform
Logger.Log(string.Format("An actor entry '{0}' on DialogueTree has no reference. A dummy Actor will be used with the entry Key for name", paramName), "Dialogue Tree", this);
return new ProxyDialogueActor(paramName, null);
}
///<summary>Set the target IDialogueActor for the provided key parameter name</summary>
public void SetActorReference(string paramName, IDialogueActor actor) {
var param = actorParameters.Find(p => p.name == paramName);
if ( param == null ) {
Logger.LogError(string.Format("There is no defined Actor key name '{0}'", paramName), "Dialogue Tree", this);
return;
}
param.actor = actor;
}
///<summary>Set all target IDialogueActors at once by provided dictionary</summary>
public void SetActorReferences(Dictionary<string, IDialogueActor> actors) {
foreach ( var pair in actors ) {
var param = actorParameters.Find(p => p.name == pair.Key);
if ( param == null ) {
Logger.LogWarning(string.Format("There is no defined Actor key name '{0}'. Seting actor skiped", pair.Key), "Dialogue Tree", this);
continue;
}
param.actor = pair.Value;
}
}
///<summary>Continues the DialogueTree at provided child connection index of currentNode</summary>
public void Continue(int index = 0) {
if ( index < 0 || index > currentNode.outConnections.Count - 1 ) {
Stop(true);
return;
}
currentNode.outConnections[index].status = Status.Success; //editor vis
EnterNode((DTNode)currentNode.outConnections[index].targetNode);
}
///<summary>Enters the provided node</summary>
public void EnterNode(DTNode node) {
currentNode = node;
currentNode.Reset(false);
if ( currentNode.Execute(agent, blackboard) == Status.Error ) {
Stop(false);
}
}
///<summary>Raise the OnSubtitlesRequest event</summary>
public static void RequestSubtitles(SubtitlesRequestInfo info) {
if ( OnSubtitlesRequest != null )
OnSubtitlesRequest(info);
else Logger.LogWarning("Subtitle Request event has no subscribers. Make sure to add the default '@DialogueGUI' prefab or create your own GUI.", "Dialogue Tree");
}
///<summary>Raise the OnMultipleChoiceRequest event</summary>
public static void RequestMultipleChoices(MultipleChoiceRequestInfo info) {
if ( OnMultipleChoiceRequest != null )
OnMultipleChoiceRequest(info);
else Logger.LogWarning("Multiple Choice Request event has no subscribers. Make sure to add the default '@DialogueGUI' prefab or create your own GUI.", "Dialogue Tree");
}
protected override void OnGraphStarted() {
previousDialogue = currentDialogue;
currentDialogue = this;
Logger.Log(string.Format("Dialogue Started '{0}'", this.name), "Dialogue Tree", this);
if ( OnDialogueStarted != null ) {
OnDialogueStarted(this);
}
if ( !( agent is IDialogueActor ) ) {
Logger.Log("Agent used in DialogueTree does not implement IDialogueActor. A dummy actor will be used.", "Dialogue Tree", this);
}
enterStartNodeFlag = true;
}
protected override void OnGraphUpdate() {
if ( enterStartNodeFlag ) {
//use a flag so that other nodes can do stuff on graph started
enterStartNodeFlag = false;
EnterNode(currentNode != null ? currentNode : (DTNode)primeNode);
}
if ( currentNode is IUpdatable ) {
( currentNode as IUpdatable ).Update();
}
}
protected override void OnGraphStoped() {
currentDialogue = previousDialogue;
previousDialogue = null;
currentNode = null;
Logger.Log(string.Format("Dialogue Finished '{0}'", this.name), "Dialogue Tree", this);
if ( OnDialogueFinished != null ) {
OnDialogueFinished(this);
}
}
protected override void OnGraphPaused() {
Logger.Log(string.Format("Dialogue Paused '{0}'", this.name), "Dialogue Tree", this);
if ( OnDialoguePaused != null ) {
OnDialoguePaused(this);
}
}
protected override void OnGraphUnpaused() {
EnterNode(currentNode != null ? currentNode : (DTNode)primeNode);
Logger.Log(string.Format("Dialogue Resumed '{0}'", this.name), "Dialogue Tree", this);
if ( OnDialogueStarted != null ) {
OnDialogueStarted(this);
}
}
///----------------------------------------------------------------------------------------------
///---------------------------------------UNITY EDITOR-------------------------------------------
#if UNITY_EDITOR
[UnityEditor.MenuItem("Tools/ParadoxNotion/NodeCanvas/Create/Dialogue Tree Object", false, 2)]
static void Editor_CreateGraph() {
var dt = new GameObject("DialogueTree").AddComponent<DialogueTreeController>();
UnityEditor.Selection.activeObject = dt;
}
#endif
}
}