Skip to content

Instantly share code, notes, and snippets.

@valkyrienyanko
Created January 29, 2023 22:10
Show Gist options
  • Save valkyrienyanko/b1355f108a05791b3acca5b1fd71e871 to your computer and use it in GitHub Desktop.
Save valkyrienyanko/b1355f108a05791b3acca5b1fd71e871 to your computer and use it in GitHub Desktop.
using Microsoft.VisualBasic;
namespace CatCafe;
public class Dialogue
{
public string Name { get; set; }
public string Text { get; set; }
public List<Choice> Choices { get; set; }
}
public class Choice
{
public string Text { get; set; }
public List<Dialogue> Dialogues { get; set; }
}
public partial class UIDialogue : Control
{
private Dictionary<string, List<Dialogue>> Conversations { get; set; } = new()
{
{ "fairy_encounter_1", new List<Dialogue>
{
new Dialogue
{
Name = "Minnie",
Text = "Who are you?"
},
new Dialogue
{
Name = "Fairy",
Text = "A fairy of course! Would you like to see something cool?",
Choices = new List<Choice>
{
{
new Choice
{
Text = "Yes",
Dialogues = new List<Dialogue>
{
new Dialogue
{
Name = "Fairy",
Text = "*fairy does a back flip*"
},
new Dialogue
{
Name = "Fairy",
Text = "*fairy laughs*"
}
}
} },
{
new Choice
{
Text = "No",
Dialogues = new List<Dialogue>
{
new Dialogue
{
Name = "Fairy",
Text = "Aw okay.."
}
}
}
}
}
},
{
new Dialogue
{
Name = "Fairy",
Text = "Okay well good bye!"
}
}
} }
};
private string CurConversation { get; set; } = "fairy_encounter_1";
private int CurDialogIndex { get; set; }
private int CurDialogWithChoicesIndex { get; set; }
private List<Dialogue> CurDialogues { get; set; }
// Anything lower than 2 is way too slow even for crazy plot dialogues
[Export(PropertyHint.Range, "2, 100")] public double TextSpeed { get; set; } = 40.0;
[Export] public NodePath NodePathActorName { get; set; }
[Export] public NodePath NodePathActorDialogue { get; set; }
[Export] public NodePath NodePathPanelName { get; set; }
[Export] public NodePath NodePathPanelBack { get; set; }
[Export] public NodePath NodePathPanelDialogue { get; set; }
[Export] public NodePath NodePathSectionChoices { get; set; }
private Sprite2D ActorPortrait { get; set; }
private Label ActorName { get; set; }
private Label ActorDialogue { get; set; }
private Control PanelName { get; set; }
private Control PanelBack { get; set; }
private Control PanelDialogue { get; set; }
private Control SectionChoices { get; set; }
private GridContainer[] ChoiceRows { get; set; } = new GridContainer[2];
private Button[] BtnChoices { get; set; } = new Button[4];
private Vector2 PanelDialogueSize { get; set; }
private Vector2 PanelBackPosition { get; set; }
private Vector2 ActorPortraitStartingPos { get; set; }
private Tween TweenPanel { get; set; }
private Tween TweenText { get; set; }
public override void _Ready()
{
ActorPortrait = GetNode<Sprite2D>("Portrait");
ActorName = GetNode<Label> (NodePathActorName);
ActorDialogue = GetNode<Label> (NodePathActorDialogue);
PanelName = GetNode<Control> (NodePathPanelName);
PanelBack = GetNode<Control> (NodePathPanelBack);
PanelDialogue = GetNode<Control> (NodePathPanelDialogue);
SectionChoices = GetNode<Control> (NodePathSectionChoices);
ChoiceRows[0] = SectionChoices.GetNode<GridContainer>("Row1");
ChoiceRows[1] = SectionChoices.GetNode<GridContainer>("Row2");
BtnChoices[0] = ChoiceRows[0].GetNode<Button>("Choice1");
BtnChoices[1] = ChoiceRows[0].GetNode<Button>("Choice2");
BtnChoices[2] = ChoiceRows[1].GetNode<Button>("Choice3");
BtnChoices[3] = ChoiceRows[1].GetNode<Button>("Choice4");
// Dialogue text should not make the panel grow above, only downwards
PanelDialogue.GrowVertical = GrowDirection.End;
// Get panel dialogue size and panel back position before setting the scale of panel dialogue to zero
PanelDialogueSize = PanelDialogue.GetRect().Size;
PanelBackPosition = PanelBack.GlobalPosition;
PanelDialogue.Scale = Vector2.Zero;
ActorPortraitStartingPos = ActorPortrait.GlobalPosition;
// Initially hide the dialogue panel
Destroy();
}
public override void _PhysicsProcess(double delta)
{
var portraitPos = ActorPortrait.GlobalPosition;
portraitPos.Y = PanelBack.GlobalPosition.Y - ActorPortrait.GetRect().Size.Y;
ActorPortrait.GlobalPosition = ActorPortrait.GlobalPosition.Lerp(portraitPos, 0.05f);
}
public override void _Input(InputEvent @event)
{
if (Input.IsActionJustPressed("player_skip_dialogue"))
{
var firstDialogue = false;
if (!Visible)
{
firstDialogue = true;
Reveal();
// Prepare dialogue for first time
CurDialogues = new List<Dialogue>();
foreach (var diag in Conversations[CurConversation])
CurDialogues.Add(new Dialogue
{
Name = diag.Name,
Text = diag.Text
});
GD.Print(CurDialogues.PrintFull());
}
NextDialogue(firstDialogue);
}
}
public void NextDialogue(bool firstDialogue)
{
// Hide the choices before each new dialogue is shown
HideChoices();
// Get the current conversation
var conversation = Conversations[CurConversation];
// Are we at the end of the conversation?
if (CurDialogIndex >= conversation.Count)
{
// Close all the dialogue UIs
CurDialogIndex = 0;
CurDialogWithChoicesIndex = 0;
Destroy();
return;
}
// Get the current dialog in the conversation
var dialog = CurDialogues[CurDialogIndex++];
// Display the dialogue
Text(dialog.Name, dialog.Text, firstDialogue ? 0.5 : 0);
var dialogWithMaybeChoices = conversation[CurDialogWithChoicesIndex++];
// Are there choices to this dialogue?
if (dialogWithMaybeChoices.Choices == null)
return;
// There are choices, lets show them
SectionChoices.Show();
// Showing all choices that were defined
for (int i = 0; i < BtnChoices.Length; i++)
if (dialogWithMaybeChoices.Choices.ElementAtOrDefault(i) != default(Choice))
{
var btn = BtnChoices[i];
var choice = dialogWithMaybeChoices.Choices[i];
btn.Show();
btn.Text = choice.Text; // Show the text for each choice
// Lets do something when we click on a choice
void onPressed()
{
// Unsubscribe as soon as we click on a choice to prepare it for later
btn.Pressed -= onPressed;
var dialogues = choice.Dialogues;
// Does this choice lead to additional dialogues?
if (dialogues != null)
{
}
}
btn.Pressed += onPressed;
}
}
private void HideChoices()
{
SectionChoices.Hide();
for (int i = 0; i < BtnChoices.Length; i++)
BtnChoices[i].Hide();
}
private void Reveal()
{
SetPhysicsProcess(true);
Show();
// ensure no other tween for panel is running
TweenPanel?.Kill();
AnimatePanel();
}
private void Text(string name, string dialogue, double delay = 0)
{
// ensure no other tween for text is running
TweenText?.Kill();
ActorName.Text = name;
ActorDialogue.Text = dialogue;
ActorDialogue.VisibleRatio = 0;
AnimateText(delay);
}
public void Destroy()
{
// exit animation for dialogue panel
TweenPanel = GetTree().CreateTween();
TweenPanel.TweenProperty(PanelDialogue, "scale", Vector2.Zero, 0.4);
// stop setting the NPC portrait position
SetPhysicsProcess(false);
// exit animation for NPC portrait
var tween = GetTree().CreateTween();
tween.TweenProperty(ActorPortrait, "position:y", ActorPortraitStartingPos.Y, 0.4);
tween.TweenCallback(Callable.From(() => Hide()));
}
private void AnimatePanel()
{
PanelDialogue.PivotOffset = PanelDialogueSize / 2;
TweenPanel = GetTree().CreateTween();
TweenPanel.TweenProperty(PanelDialogue, "scale", Vector2.One, 0.5)
.From(Vector2.Zero)
.SetTrans(Tween.TransitionType.Quad)
.SetEase(Tween.EaseType.InOut);
}
private void AnimateText(double delay)
{
// allow 1 second for every 'TextSpeed' characters of text
var duration = ActorDialogue.Text.Length / TextSpeed;
TweenText = GetTree().CreateTween();
TweenText.TweenProperty(ActorDialogue, "visible_ratio", 1, duration)
.SetDelay(delay)
.From(0.0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment