using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using Framework.Services;
using Framework.Support;
using ConditionalAttribute = System.Diagnostics.ConditionalAttribute;
namespace Framework.Dialogue {
/// Manages UI widgets in a canvas used to display dialogue
///
/// This class is intended to help implement the IDialogueCanvas interface based
/// and apply common standards to how UI elements need to be named to be
/// discoverable by the dialogue canvas.
///
[RequireComponent(typeof(Canvas))]
public class DialogueCanvas : ScriptComponent, IDialogueCanvas {
#region class ButtonEventProxy
/// Forwards button events together with the assigned button index
private class ButtonEventProxy : IDisposable {
/// Creates a new button proxy and subscribes it to a button
/// Canvas to which the button events will be delivered
/// Button this proxy will forward events for
/// Index of option this button represents
/// A new button event proxy that has been subscribed to the button
public static ButtonEventProxy CreateAndSubscribe(
DialogueCanvas canvas, Button button, int buttonIndex
) {
var proxy = new ButtonEventProxy(canvas, button, buttonIndex);
button.onClick.AddListener(proxy.clickedDelegate);
return proxy;
}
/// Initializes a new proxy for specified button with index
/// Canvas to which button events will be sent
/// Button for which the proxy will forward events
/// Button index that will be reported to the canvas
private ButtonEventProxy(DialogueCanvas canvas, Button button, int buttonIndex) {
this.clickedDelegate = new UnityAction(clicked);
this.canvas = canvas;
this.buttonIndex = buttonIndex;
this.button = button;
}
/// Unsubsribes from the buttons events
public void Dispose() {
if(this.button != null) {
this.button.onClick.RemoveListener(this.clickedDelegate);
this.button = null;
}
this.canvas = null;
}
/// Callback invoked when the user has clicked on the button
private void clicked() {
if(this.canvas != null) {
this.canvas.Select(this.buttonIndex);
}
}
/// Canvas to which button click events will be sent
private DialogueCanvas canvas;
/// Button to whose click event the proxy is subscribed
private Button button;
/// Button index the proxy will report to the canvas
private int buttonIndex;
/// Delete for the clicked() method
private UnityAction clickedDelegate;
}
#endregion // class ButtonEventProxy
/// Triggered when the user picks a choice or confirms the speech
public event Action Selected;
/// Displays text spoken or thought by a character
/// Text that will be displayed
public void ShowSpeech(string text) {
ShowSpeechAndChoices(text, null);
}
/// Lets the player make a choice
/// Choices the player can select from
public void ShowChoices(string[] choices) {
ShowSpeechAndChoices(null, choices);
}
///
/// Displays text spoken or thought by a character (or the player) and lets
/// the player make a choice
///
/// Text that will be displayed
/// Choices the player can select from
public void ShowSpeechAndChoices(string speech, string[] choices) {
// Show or hide the speech panel as needed
if(string.IsNullOrEmpty(speech)) {
if(this.speechVisible) {
TearDownSpeech();
this.speechVisible = false;
}
} else {
SetupNewSpeech(speech);
this.speechVisible = true;
}
int choiceCount = ArrayHelper.CountNullableArray(choices);
warnIfTooManyButtons(choiceCount);
// Show or hide the choice buttons as needed
if(choiceCount > 0) {
SetupNewChoices(choices);
this.selectableChoiceCount = choiceCount;
} else if(this.selectableChoiceCount > 0) {
TearDownChoices();
this.selectableChoiceCount = 0;
}
}
/// Hides the dialogue from the canvas
public void Hide() {
ShowSpeechAndChoices(null, null);
}
/// Picks a choice as if the user had clicked on a choice button
/// Index of the choice that has been selected
public void Select(int choiceIndex) {
// If a choice was selected while the choice were not visible,
// ignore this notification. Probably the user pressed a keyboard
// shortcut when no choices were visible or double-selected a choice.
if(choiceIndex >= this.selectableChoiceCount) {
Debug.LogWarning(
"Ignoring choice " + choiceIndex + " because no choice by that index " +
"is currently being presented."
);
return;
}
// If there are choices being displayed, require one of them to be selected
// (the user cannot continue to next speech panel if he's asked for a choice).
if(this.selectableChoiceCount > 0) {
if(choiceIndex == -1) {
Debug.LogWarning(
"Ignoring choice -1 (speech panel) because there are choices available"
);
return;
}
// Having picked a choice, kill the choice buttons
TearDownChoices();
this.selectableChoiceCount = 0;
}
// The user made a valid choice or confirmed the speech panel when
// it was the only widget being shown.
OnSelected(choiceIndex);
}
/// Called once before the first update cycle of the component
protected override void Awake() {
base.Awake();
// This component needs to be attached to a canvas
this.canvas = GetComponent