#if HAVE_NODECANVAS
using System;
using UnityEngine;
using ParadoxNotion.Design;
using NodeCanvas.Framework;
using Framework.Cameras;
using Framework.Services;
namespace Framework.Dialogue {
/// Displays dialogue to the user
[Category("Dialogue")]
[Icon("Dialogue")]
[Description("Display speech to the player in a dialogue UI")]
public class DisplayDialogueTask : ActionTask {
/// On whom the dialogue UI will be opened
[RequiredField]
public BBParameter DialogueOwner;
/// Text that will be displayed as speech in the dialogue UI
[Multiline, TextAreaField(50)]
public string Text;
/// Choices the user can make
public string[] Choices;
///
/// Called when the instance is first activated to provide it with any
/// external services it relies on.
///
/// Camera manager that provides the active camera
///
/// Dialogue manager through which the dialogue UI will be displayed
///
protected virtual void Inject(
ICameraManager cameraManager, IDialogueManager dialogueManager
) {
this.cameraManager = cameraManager;
this.dialogueManager = dialogueManager;
}
/// Provides a summary description of what this action does
protected override string info {
get {
int choiceCount = countChoices();
string text = "\"" + this.Text + "\"";
if(choiceCount > 0) {
text += "\n";
}
for(int index = 0; index < choiceCount; ++index) {
text += "\n[" + this.Choices[index] + "]";
}
return text;
}
}
/// Called only once before the action is executed for the first time
/// Null if everything worked out, an error message in case of an error
protected override string OnInit() {
this.dialogueChoiceSelectedDelegate = new Action(dialogueChoiceSelected);
storeChoiceIndexOnBlackboard(-1);
return base.OnInit();
}
/// Called once when the action is executed
protected override void OnExecute() {
ServiceInjector.InjectDependencies(this);
// We have to expect that this state leaves via choice condition tasks, so clear
// the previously made dialogue choice to prevent the conditions from triggering
// until the user has selected a choice for *this* dialogue.
removeChoiceIndexOnBlackboard();
// Fetch for the currently active camera to search for a placement the dialogue
UnityEngine.Camera activeCamera = getActiveCamera();
if(activeCamera == null) {
Debug.LogError("Could not determine active camera");
return;
}
// Let tThe dialogue manager score the available placements and look for
// the one that is best visible to the player.
DialoguePlacement placement = this.dialogueManager.FindPlacementOnActor(
activeCamera.transform, this.DialogueOwner.value.gameObject
);
#if false
if(placement == null) {
Debug.LogWarning(
"Could not find a suitable dialogue UI placement on '" + this.DialogueOwner.name + "' " +
"(GameObject = '" + this.DialogueOwner.value.name + "'), " +
"good luck, player, you're stuck!"
);
// Coup de grace. Bail out of the task even though the dialogue wasn't shown.
EndAction();
return;
}
#endif
// Show the dialogue and subscribe to the
this.canvas = this.dialogueManager.ShowDialogue(placement);
this.canvas.ShowSpeechAndChoices(this.Text, this.Choices);
this.canvas.Selected += this.dialogueChoiceSelectedDelegate;
}
/// Called when the action ends due to any reason
protected override void OnStop() {
if(this.canvas != null) {
this.canvas.Selected -= this.dialogueChoiceSelectedDelegate;
this.canvas = null;
// Do not hide the canvas, more dialogue may be following
}
}
/// Called when the player selects a choice in the dialogue
/// Index of the choice the player has selected
private void dialogueChoiceSelected(int choiceIndex) {
storeChoiceIndexOnBlackboard(choiceIndex);
EndAction();
}
/// Removes the index of the selected dialogue choice from the blackboard
private void removeChoiceIndexOnBlackboard() {
const string ChoiceIndexVariableName = "DialogueChoiceIndex";
IBlackboard choiceBlackboard = ownerBlackboard;
if(choiceBlackboard == null) {
Debug.LogError(
"DialogueChoiceCondition could not find a blackboard in its state machine. " +
"A blackboard is required to store the choices made by the player. " +
"Conditions on multiple choice dialogue will not work."
);
return;
}
// If it's null, we'll already have complaine in OnInit()!
Variable choiceIndexVariable = choiceBlackboard.GetVariable(
ChoiceIndexVariableName, typeof(int)
);
if(choiceIndexVariable != null) {
choiceBlackboard.RemoveVariable(ChoiceIndexVariableName);
}
}
/// Stores the index of the selected dialogue choice on the blackboard
/// Index that will be stored on the blackboard
private void storeChoiceIndexOnBlackboard(int choiceIndex) {
const string ChoiceIndexVariableName = "DialogueChoiceIndex";
IBlackboard choiceBlackboard = ownerBlackboard;
if(choiceBlackboard == null) {
Debug.LogError(
"DisplayDialogueAction could not find a blackboard in its state machine. " +
"A blackboard is required to store the choices made by the player. " +
"Conditions on multiple choice dialogue will not work."
);
return;
}
// If it's null, we'll already have complaine in OnInit()!
Variable choiceIndexVariable = choiceBlackboard.GetVariable(
ChoiceIndexVariableName, typeof(int)
);
if(choiceIndexVariable == null) {
choiceIndexVariable = choiceBlackboard.AddVariable(ChoiceIndexVariableName, typeof(int));
}
choiceIndexVariable.value = choiceIndex;
}
/// Looks for the currently active camera
/// The currently active camera or null if it couldn't be determined
private Camera getActiveCamera() {
Camera activeCamera;
{
if(this.cameraManager != null) {
activeCamera = this.cameraManager.ActiveCamera;
} else {
activeCamera = null;
}
if(activeCamera == null) {
activeCamera = UnityEngine.Camera.current;
}
if(activeCamera == null) {
activeCamera = UnityEngine.Camera.main;
}
}
return activeCamera;
}
/// Counts the number of choices the user can choose from
/// The number of choices available to the user
private int countChoices() {
if(this.Choices == null) {
return 0;
} else {
return this.Choices.Length;
}
}
/// Camera manager through which the scene is being viewed
[NonSerialized]
private ICameraManager cameraManager;
/// Manages the currently active dialogue UI in the game
[NonSerialized]
private IDialogueManager dialogueManager;
/// Currently active dialogue canvas
[NonSerialized]
private IDialogueCanvas canvas;
/// Called when the player selcects a dialogue choice
[NonSerialized]
private Action dialogueChoiceSelectedDelegate;
}
} // namespace Framework.Dialogue
#endif // HAVE_NODECANVAS