#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