first commit
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user