Add ability to hit NPCs and see different dialogue, add interrupt feature for dialogue controller singleton
This commit is contained in:
@@ -8,416 +8,419 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DialogueManagerRuntime
|
||||
{
|
||||
public enum TranslationSource
|
||||
public enum TranslationSource
|
||||
{
|
||||
None,
|
||||
Guess,
|
||||
CSV,
|
||||
PO
|
||||
}
|
||||
|
||||
public partial class DialogueManager : Node
|
||||
{
|
||||
public delegate void PassedTitleEventHandler(string title);
|
||||
public delegate void GotDialogueEventHandler(DialogueLine dialogueLine);
|
||||
public delegate void MutatedEventHandler(Dictionary mutation);
|
||||
public delegate void DialogueEndedEventHandler(Resource dialogueResource);
|
||||
|
||||
public static PassedTitleEventHandler? PassedTitle;
|
||||
public static GotDialogueEventHandler? GotDialogue;
|
||||
public static MutatedEventHandler? Mutated;
|
||||
public static DialogueEndedEventHandler? DialogueEnded;
|
||||
|
||||
[Signal] public delegate void ResolvedEventHandler(Variant value);
|
||||
|
||||
private static GodotObject? instance;
|
||||
public static GodotObject Instance
|
||||
{
|
||||
None,
|
||||
Guess,
|
||||
CSV,
|
||||
PO
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = Engine.GetSingleton("DialogueManager");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DialogueManager : Node
|
||||
|
||||
public static Godot.Collections.Array GameStates
|
||||
{
|
||||
public delegate void PassedTitleEventHandler(string title);
|
||||
public delegate void GotDialogueEventHandler(DialogueLine dialogueLine);
|
||||
public delegate void MutatedEventHandler(Dictionary mutation);
|
||||
public delegate void DialogueEndedEventHandler(Resource dialogueResource);
|
||||
|
||||
public static PassedTitleEventHandler? PassedTitle;
|
||||
public static GotDialogueEventHandler? GotDialogue;
|
||||
public static MutatedEventHandler? Mutated;
|
||||
public static DialogueEndedEventHandler? DialogueEnded;
|
||||
|
||||
[Signal] public delegate void ResolvedEventHandler(Variant value);
|
||||
|
||||
private static GodotObject? instance;
|
||||
public static GodotObject Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = Engine.GetSingleton("DialogueManager");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
get => (Godot.Collections.Array)Instance.Get("game_states");
|
||||
set => Instance.Set("game_states", value);
|
||||
}
|
||||
|
||||
|
||||
public static Godot.Collections.Array GameStates
|
||||
{
|
||||
get => (Godot.Collections.Array)Instance.Get("game_states");
|
||||
set => Instance.Set("game_states", value);
|
||||
}
|
||||
public static bool IncludeSingletons
|
||||
{
|
||||
get => (bool)Instance.Get("include_singletons");
|
||||
set => Instance.Set("include_singletons", value);
|
||||
}
|
||||
|
||||
|
||||
public static bool IncludeSingletons
|
||||
{
|
||||
get => (bool)Instance.Get("include_singletons");
|
||||
set => Instance.Set("include_singletons", value);
|
||||
}
|
||||
public static bool IncludeClasses
|
||||
{
|
||||
get => (bool)Instance.Get("include_classes");
|
||||
set => Instance.Set("include_classes", value);
|
||||
}
|
||||
|
||||
|
||||
public static bool IncludeClasses
|
||||
{
|
||||
get => (bool)Instance.Get("include_classes");
|
||||
set => Instance.Set("include_classes", value);
|
||||
}
|
||||
public static TranslationSource TranslationSource
|
||||
{
|
||||
get => (TranslationSource)(int)Instance.Get("translation_source");
|
||||
set => Instance.Set("translation_source", (int)value);
|
||||
}
|
||||
|
||||
|
||||
public static TranslationSource TranslationSource
|
||||
{
|
||||
get => (TranslationSource)(int)Instance.Get("translation_source");
|
||||
set => Instance.Set("translation_source", (int)value);
|
||||
}
|
||||
public static Func<Node> GetCurrentScene
|
||||
{
|
||||
set => Instance.Set("get_current_scene", Callable.From(value));
|
||||
}
|
||||
|
||||
|
||||
public static Func<Node> GetCurrentScene
|
||||
{
|
||||
set => Instance.Set("get_current_scene", Callable.From(value));
|
||||
}
|
||||
public void Prepare()
|
||||
{
|
||||
Instance.Connect("passed_title", Callable.From((string title) => PassedTitle?.Invoke(title)));
|
||||
Instance.Connect("got_dialogue", Callable.From((RefCounted line) => GotDialogue?.Invoke(new DialogueLine(line))));
|
||||
Instance.Connect("mutated", Callable.From((Dictionary mutation) => Mutated?.Invoke(mutation)));
|
||||
Instance.Connect("dialogue_ended", Callable.From((Resource dialogueResource) => DialogueEnded?.Invoke(dialogueResource)));
|
||||
}
|
||||
|
||||
|
||||
public void Prepare()
|
||||
{
|
||||
Instance.Connect("passed_title", Callable.From((string title) => PassedTitle?.Invoke(title)));
|
||||
Instance.Connect("got_dialogue", Callable.From((RefCounted line) => GotDialogue?.Invoke(new DialogueLine(line))));
|
||||
Instance.Connect("mutated", Callable.From((Dictionary mutation) => Mutated?.Invoke(mutation)));
|
||||
Instance.Connect("dialogue_ended", Callable.From((Resource dialogueResource) => DialogueEnded?.Invoke(dialogueResource)));
|
||||
}
|
||||
public static async Task<GodotObject> GetSingleton()
|
||||
{
|
||||
if (instance != null)
|
||||
return instance;
|
||||
|
||||
var tree = Engine.GetMainLoop();
|
||||
int x = 0;
|
||||
|
||||
// Try and find the singleton for a few seconds
|
||||
while (!Engine.HasSingleton("DialogueManager") && x < 300)
|
||||
{
|
||||
await tree.ToSignal(tree, SceneTree.SignalName.ProcessFrame);
|
||||
x++;
|
||||
}
|
||||
|
||||
// If it times out something is wrong
|
||||
if (x >= 300)
|
||||
{
|
||||
throw new Exception("The DialogueManager singleton is missing.");
|
||||
}
|
||||
|
||||
instance = Engine.GetSingleton("DialogueManager");
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static async Task<DialogueLine?> GetNextDialogueLine(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
Instance.Call("_bridge_get_next_dialogue_line", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
var result = await Instance.ToSignal(Instance, "bridge_get_next_dialogue_line_completed");
|
||||
|
||||
if ((RefCounted)result[0] == null)
|
||||
return null;
|
||||
|
||||
return new DialogueLine((RefCounted)result[0]);
|
||||
}
|
||||
|
||||
|
||||
public static async Task<GodotObject> GetSingleton()
|
||||
{
|
||||
if (instance != null) return instance;
|
||||
|
||||
var tree = Engine.GetMainLoop();
|
||||
int x = 0;
|
||||
|
||||
// Try and find the singleton for a few seconds
|
||||
while (!Engine.HasSingleton("DialogueManager") && x < 300)
|
||||
{
|
||||
await tree.ToSignal(tree, SceneTree.SignalName.ProcessFrame);
|
||||
x++;
|
||||
}
|
||||
|
||||
// If it times out something is wrong
|
||||
if (x >= 300)
|
||||
{
|
||||
throw new Exception("The DialogueManager singleton is missing.");
|
||||
}
|
||||
|
||||
instance = Engine.GetSingleton("DialogueManager");
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static async Task<DialogueLine?> GetNextDialogueLine(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
Instance.Call("_bridge_get_next_dialogue_line", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
var result = await Instance.ToSignal(Instance, "bridge_get_next_dialogue_line_completed");
|
||||
|
||||
if ((RefCounted)result[0] == null) return null;
|
||||
|
||||
return new DialogueLine((RefCounted)result[0]);
|
||||
}
|
||||
public static CanvasLayer ShowExampleDialogueBalloon(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (CanvasLayer)Instance.Call("show_example_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
|
||||
public static CanvasLayer ShowExampleDialogueBalloon(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (CanvasLayer)Instance.Call("show_example_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
public static Node ShowDialogueBalloonScene(string balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
public static Node ShowDialogueBalloonScene(PackedScene balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
public static Node ShowDialogueBalloonScene(Node balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
|
||||
public static Node ShowDialogueBalloonScene(string balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
public static Node ShowDialogueBalloonScene(PackedScene balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
public static Node ShowDialogueBalloonScene(Node balloonScene, Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon_scene", balloonScene, dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
public static Node ShowDialogueBalloon(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
|
||||
|
||||
public static Node ShowDialogueBalloon(Resource dialogueResource, string key = "", Array<Variant>? extraGameStates = null)
|
||||
{
|
||||
return (Node)Instance.Call("show_dialogue_balloon", dialogueResource, key, extraGameStates ?? new Array<Variant>());
|
||||
}
|
||||
public static async void Mutate(Dictionary mutation, Array<Variant>? extraGameStates = null, bool isInlineMutation = false)
|
||||
{
|
||||
Instance.Call("_bridge_mutate", mutation, extraGameStates ?? new Array<Variant>(), isInlineMutation);
|
||||
await Instance.ToSignal(Instance, "bridge_mutated");
|
||||
}
|
||||
|
||||
|
||||
public static async void Mutate(Dictionary mutation, Array<Variant>? extraGameStates = null, bool isInlineMutation = false)
|
||||
{
|
||||
Instance.Call("_bridge_mutate", mutation, extraGameStates ?? new Array<Variant>(), isInlineMutation);
|
||||
await Instance.ToSignal(Instance, "bridge_mutated");
|
||||
}
|
||||
|
||||
|
||||
public bool ThingHasMethod(GodotObject thing, string method)
|
||||
{
|
||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
||||
return info != null;
|
||||
}
|
||||
public bool ThingHasMethod(GodotObject thing, string method)
|
||||
{
|
||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
||||
return info != null;
|
||||
}
|
||||
|
||||
|
||||
public async void ResolveThingMethod(GodotObject thing, string method, Array<Variant> args)
|
||||
{
|
||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
||||
MethodInfo? info = thing.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
if (info == null) return;
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
#nullable disable
|
||||
// Convert the method args to something reflection can handle
|
||||
ParameterInfo[] argTypes = info.GetParameters();
|
||||
object[] _args = new object[argTypes.Length];
|
||||
for (int i = 0; i < argTypes.Length; i++)
|
||||
// Convert the method args to something reflection can handle
|
||||
ParameterInfo[] argTypes = info.GetParameters();
|
||||
object[] _args = new object[argTypes.Length];
|
||||
for (int i = 0; i < argTypes.Length; i++)
|
||||
{
|
||||
// check if args is assignable from derived type
|
||||
if (i < args.Count && args[i].Obj != null)
|
||||
{
|
||||
// check if args is assignable from derived type
|
||||
if (i < args.Count && args[i].Obj != null)
|
||||
{
|
||||
if (argTypes[i].ParameterType.IsAssignableFrom(args[i].Obj.GetType()))
|
||||
{
|
||||
_args[i] = args[i].Obj;
|
||||
}
|
||||
// fallback to assigning primitive types
|
||||
else
|
||||
{
|
||||
_args[i] = Convert.ChangeType(args[i].Obj, argTypes[i].ParameterType);
|
||||
}
|
||||
}
|
||||
else if (argTypes[i].DefaultValue != null)
|
||||
{
|
||||
_args[i] = argTypes[i].DefaultValue;
|
||||
}
|
||||
if (argTypes[i].ParameterType.IsAssignableFrom(args[i].Obj.GetType()))
|
||||
{
|
||||
_args[i] = args[i].Obj;
|
||||
}
|
||||
// fallback to assigning primitive types
|
||||
else
|
||||
{
|
||||
_args[i] = Convert.ChangeType(args[i].Obj, argTypes[i].ParameterType);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a single frame wait in case the method returns before signals can listen
|
||||
await ToSignal(Engine.GetMainLoop(), SceneTree.SignalName.ProcessFrame);
|
||||
|
||||
// invoke method and handle the result based on return type
|
||||
object result = info.Invoke(thing, _args);
|
||||
|
||||
if (result is Task taskResult)
|
||||
else if (argTypes[i].DefaultValue != null)
|
||||
{
|
||||
// await Tasks and handle result if it is a Task<T>
|
||||
await taskResult;
|
||||
var taskType = taskResult.GetType();
|
||||
if (taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
var resultProperty = taskType.GetProperty("Result");
|
||||
var taskResultValue = resultProperty.GetValue(taskResult);
|
||||
EmitSignal(SignalName.Resolved, (Variant)taskResultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSignal(SignalName.Resolved, null);
|
||||
}
|
||||
_args[i] = argTypes[i].DefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a single frame wait in case the method returns before signals can listen
|
||||
await ToSignal(Engine.GetMainLoop(), SceneTree.SignalName.ProcessFrame);
|
||||
|
||||
// invoke method and handle the result based on return type
|
||||
object result = info.Invoke(thing, _args);
|
||||
|
||||
if (result is Task taskResult)
|
||||
{
|
||||
// await Tasks and handle result if it is a Task<T>
|
||||
await taskResult;
|
||||
var taskType = taskResult.GetType();
|
||||
if (taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
var resultProperty = taskType.GetProperty("Result");
|
||||
var taskResultValue = resultProperty.GetValue(taskResult);
|
||||
EmitSignal(SignalName.Resolved, (Variant)taskResultValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSignal(SignalName.Resolved, (Variant)result);
|
||||
EmitSignal(SignalName.Resolved, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSignal(SignalName.Resolved, (Variant)result);
|
||||
}
|
||||
}
|
||||
#nullable enable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class DialogueLine : RefCounted
|
||||
public partial class DialogueLine : RefCounted
|
||||
{
|
||||
private string id = "";
|
||||
public string Id
|
||||
{
|
||||
private string id = "";
|
||||
public string Id
|
||||
{
|
||||
get => id;
|
||||
set => id = value;
|
||||
}
|
||||
|
||||
private string type = "dialogue";
|
||||
public string Type
|
||||
{
|
||||
get => type;
|
||||
set => type = value;
|
||||
}
|
||||
|
||||
private string next_id = "";
|
||||
public string NextId
|
||||
{
|
||||
get => next_id;
|
||||
set => next_id = value;
|
||||
}
|
||||
|
||||
private string character = "";
|
||||
public string Character
|
||||
{
|
||||
get => character;
|
||||
set => character = value;
|
||||
}
|
||||
|
||||
private string text = "";
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
set => text = value;
|
||||
}
|
||||
|
||||
private string translation_key = "";
|
||||
public string TranslationKey
|
||||
{
|
||||
get => translation_key;
|
||||
set => translation_key = value;
|
||||
}
|
||||
|
||||
private Array<DialogueResponse> responses = new Array<DialogueResponse>();
|
||||
public Array<DialogueResponse> Responses
|
||||
{
|
||||
get => responses;
|
||||
}
|
||||
|
||||
private string? time = null;
|
||||
public string? Time
|
||||
{
|
||||
get => time;
|
||||
}
|
||||
|
||||
private Dictionary pauses = new Dictionary();
|
||||
public Dictionary Pauses
|
||||
{
|
||||
get => pauses;
|
||||
}
|
||||
|
||||
private Dictionary speeds = new Dictionary();
|
||||
public Dictionary Speeds
|
||||
{
|
||||
get => speeds;
|
||||
}
|
||||
|
||||
private Array<Godot.Collections.Array> inline_mutations = new Array<Godot.Collections.Array>();
|
||||
public Array<Godot.Collections.Array> InlineMutations
|
||||
{
|
||||
get => inline_mutations;
|
||||
}
|
||||
|
||||
private Array<Variant> extra_game_states = new Array<Variant>();
|
||||
|
||||
private Array<string> tags = new Array<string>();
|
||||
public Array<string> Tags
|
||||
{
|
||||
get => tags;
|
||||
}
|
||||
|
||||
public DialogueLine(RefCounted data)
|
||||
{
|
||||
type = (string)data.Get("type");
|
||||
next_id = (string)data.Get("next_id");
|
||||
character = (string)data.Get("character");
|
||||
text = (string)data.Get("text");
|
||||
translation_key = (string)data.Get("translation_key");
|
||||
pauses = (Dictionary)data.Get("pauses");
|
||||
speeds = (Dictionary)data.Get("speeds");
|
||||
inline_mutations = (Array<Godot.Collections.Array>)data.Get("inline_mutations");
|
||||
time = (string)data.Get("time");
|
||||
tags = (Array<string>)data.Get("tags");
|
||||
|
||||
foreach (var response in (Array<RefCounted>)data.Get("responses"))
|
||||
{
|
||||
responses.Add(new DialogueResponse(response));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string GetTagValue(string tagName)
|
||||
{
|
||||
string wrapped = $"{tagName}=";
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (tag.StartsWith(wrapped))
|
||||
{
|
||||
return tag.Substring(wrapped.Length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "dialogue":
|
||||
return $"<DialogueLine character=\"{character}\" text=\"{text}\">";
|
||||
case "mutation":
|
||||
return "<DialogueLine mutation>";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
get => id;
|
||||
set => id = value;
|
||||
}
|
||||
|
||||
|
||||
public partial class DialogueResponse : RefCounted
|
||||
private string type = "dialogue";
|
||||
public string Type
|
||||
{
|
||||
private string next_id = "";
|
||||
public string NextId
|
||||
{
|
||||
get => next_id;
|
||||
set => next_id = value;
|
||||
}
|
||||
|
||||
private bool is_allowed = true;
|
||||
public bool IsAllowed
|
||||
{
|
||||
get => is_allowed;
|
||||
set => is_allowed = value;
|
||||
}
|
||||
|
||||
private string text = "";
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
set => text = value;
|
||||
}
|
||||
|
||||
private string translation_key = "";
|
||||
public string TranslationKey
|
||||
{
|
||||
get => translation_key;
|
||||
set => translation_key = value;
|
||||
}
|
||||
|
||||
private Array<string> tags = new Array<string>();
|
||||
public Array<string> Tags
|
||||
{
|
||||
get => tags;
|
||||
}
|
||||
|
||||
public DialogueResponse(RefCounted data)
|
||||
{
|
||||
next_id = (string)data.Get("next_id");
|
||||
is_allowed = (bool)data.Get("is_allowed");
|
||||
text = (string)data.Get("text");
|
||||
translation_key = (string)data.Get("translation_key");
|
||||
tags = (Array<string>)data.Get("tags");
|
||||
}
|
||||
|
||||
public string GetTagValue(string tagName)
|
||||
{
|
||||
string wrapped = $"{tagName}=";
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (tag.StartsWith(wrapped))
|
||||
{
|
||||
return tag.Substring(wrapped.Length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"<DialogueResponse text=\"{text}\"";
|
||||
}
|
||||
get => type;
|
||||
set => type = value;
|
||||
}
|
||||
|
||||
private string next_id = "";
|
||||
public string NextId
|
||||
{
|
||||
get => next_id;
|
||||
set => next_id = value;
|
||||
}
|
||||
|
||||
private string character = "";
|
||||
public string Character
|
||||
{
|
||||
get => character;
|
||||
set => character = value;
|
||||
}
|
||||
|
||||
private string text = "";
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
set => text = value;
|
||||
}
|
||||
|
||||
private string translation_key = "";
|
||||
public string TranslationKey
|
||||
{
|
||||
get => translation_key;
|
||||
set => translation_key = value;
|
||||
}
|
||||
|
||||
private Array<DialogueResponse> responses = new Array<DialogueResponse>();
|
||||
public Array<DialogueResponse> Responses
|
||||
{
|
||||
get => responses;
|
||||
}
|
||||
|
||||
private string? time = null;
|
||||
public string? Time
|
||||
{
|
||||
get => time;
|
||||
}
|
||||
|
||||
private Dictionary pauses = new Dictionary();
|
||||
public Dictionary Pauses
|
||||
{
|
||||
get => pauses;
|
||||
}
|
||||
|
||||
private Dictionary speeds = new Dictionary();
|
||||
public Dictionary Speeds
|
||||
{
|
||||
get => speeds;
|
||||
}
|
||||
|
||||
private Array<Godot.Collections.Array> inline_mutations = new Array<Godot.Collections.Array>();
|
||||
public Array<Godot.Collections.Array> InlineMutations
|
||||
{
|
||||
get => inline_mutations;
|
||||
}
|
||||
|
||||
private Array<Variant> extra_game_states = new Array<Variant>();
|
||||
|
||||
private Array<string> tags = new Array<string>();
|
||||
public Array<string> Tags
|
||||
{
|
||||
get => tags;
|
||||
}
|
||||
|
||||
public DialogueLine(RefCounted data)
|
||||
{
|
||||
type = (string)data.Get("type");
|
||||
next_id = (string)data.Get("next_id");
|
||||
character = (string)data.Get("character");
|
||||
text = (string)data.Get("text");
|
||||
translation_key = (string)data.Get("translation_key");
|
||||
pauses = (Dictionary)data.Get("pauses");
|
||||
speeds = (Dictionary)data.Get("speeds");
|
||||
inline_mutations = (Array<Godot.Collections.Array>)data.Get("inline_mutations");
|
||||
time = (string)data.Get("time");
|
||||
tags = (Array<string>)data.Get("tags");
|
||||
|
||||
foreach (var response in (Array<RefCounted>)data.Get("responses"))
|
||||
{
|
||||
responses.Add(new DialogueResponse(response));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string GetTagValue(string tagName)
|
||||
{
|
||||
string wrapped = $"{tagName}=";
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (tag.StartsWith(wrapped))
|
||||
{
|
||||
return tag.Substring(wrapped.Length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "dialogue":
|
||||
return $"<DialogueLine character=\"{character}\" text=\"{text}\">";
|
||||
case "mutation":
|
||||
return "<DialogueLine mutation>";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class DialogueResponse : RefCounted
|
||||
{
|
||||
private string next_id = "";
|
||||
public string NextId
|
||||
{
|
||||
get => next_id;
|
||||
set => next_id = value;
|
||||
}
|
||||
|
||||
private bool is_allowed = true;
|
||||
public bool IsAllowed
|
||||
{
|
||||
get => is_allowed;
|
||||
set => is_allowed = value;
|
||||
}
|
||||
|
||||
private string text = "";
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
set => text = value;
|
||||
}
|
||||
|
||||
private string translation_key = "";
|
||||
public string TranslationKey
|
||||
{
|
||||
get => translation_key;
|
||||
set => translation_key = value;
|
||||
}
|
||||
|
||||
private Array<string> tags = new Array<string>();
|
||||
public Array<string> Tags
|
||||
{
|
||||
get => tags;
|
||||
}
|
||||
|
||||
public DialogueResponse(RefCounted data)
|
||||
{
|
||||
next_id = (string)data.Get("next_id");
|
||||
is_allowed = (bool)data.Get("is_allowed");
|
||||
text = (string)data.Get("text");
|
||||
translation_key = (string)data.Get("translation_key");
|
||||
tags = (Array<string>)data.Get("tags");
|
||||
}
|
||||
|
||||
public string GetTagValue(string tagName)
|
||||
{
|
||||
string wrapped = $"{tagName}=";
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (tag.StartsWith(wrapped))
|
||||
{
|
||||
return tag.Substring(wrapped.Length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"<DialogueResponse text=\"{text}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ Throw={
|
||||
|
||||
[internationalization]
|
||||
|
||||
locale/translations_pot_files=PackedStringArray("res://src/dialog/Dialogue.dialogue", "res://src/ui/dialogue/FloorExit.dialogue", "res://src/npc/rat/ratdialogue.dialogue")
|
||||
locale/translations_pot_files=PackedStringArray("res://src/dialog/Dialogue.dialogue", "res://src/ui/dialogue/FloorExit.dialogue", "res://src/npc/rat/ratdialogue.dialogue", "res://src/ui/dialogue/DialogueControl.dialogue")
|
||||
|
||||
[layer_names]
|
||||
|
||||
|
||||
@@ -5,8 +5,24 @@ public partial class DialogueController : Node
|
||||
{
|
||||
public static PackedScene DialogueBalloon { get; set; }
|
||||
|
||||
public DialogueController()
|
||||
private static Node _currentlyActiveDialogue { get; set; } = new Node();
|
||||
|
||||
private static Resource _currentDialogue { get; set; }
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
DialogueBalloon = GD.Load<PackedScene>("res://src/ui/dialogue/DialogueBalloon.tscn");
|
||||
}
|
||||
|
||||
public static void ShowDialogue(Resource dialogueResource, string dialogueTitle)
|
||||
{
|
||||
Interrupt();
|
||||
_currentlyActiveDialogue = DialogueManager.ShowDialogueBalloonScene(DialogueBalloon, dialogueResource, dialogueTitle);
|
||||
}
|
||||
|
||||
public static void Interrupt()
|
||||
{
|
||||
if (IsInstanceValid(_currentlyActiveDialogue))
|
||||
_currentlyActiveDialogue.QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public partial class Game : Node3D, IGame
|
||||
GameRepo.Pause();
|
||||
DialogueManager.GetCurrentScene = (() => this);
|
||||
var dialogueResource = GD.Load<Resource>("res://src/ui/dialogue/FloorExit.dialogue");
|
||||
DialogueManager.ShowDialogueBalloonScene(DialogueController.DialogueBalloon, dialogueResource, "floor_exit");
|
||||
DialogueController.ShowDialogue(dialogueResource, "floor_exit");
|
||||
DialogueManager.DialogueEnded += (Resource resource) => { GameRepo.Resume(); };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://1fl6s352e2ej"]
|
||||
[gd_scene load_steps=9 format=3 uid="uid://1fl6s352e2ej"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/items/throwable/ThrowableItem.cs" id="1_nac2l"]
|
||||
[ext_resource type="Resource" uid="uid://bph8c6by4s047" path="res://src/items/throwable/resources/GeomanticDice.tres" id="2_pefeg"]
|
||||
[ext_resource type="Script" path="res://src/hitbox/Hitbox.cs" id="3_qpunu"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_qihtb"]
|
||||
@@ -48,6 +49,7 @@ _data = {
|
||||
|
||||
[node name="ThrowableItem" type="Node3D"]
|
||||
script = ExtResource("1_nac2l")
|
||||
ThrowableItemInfo = ExtResource("2_pefeg")
|
||||
|
||||
[node name="Hitbox" type="Area3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -12,26 +12,10 @@ public partial class Overworld : Node3D, IDungeonFloor
|
||||
[Dependency]
|
||||
public IGameRepo GameRepo => this.DependOn<IGameRepo>();
|
||||
|
||||
[Node] public Area3D NPCBox { get; set; } = default!;
|
||||
|
||||
[Node] public Marker3D PlayerSpawnPoint { get; set; } = default!;
|
||||
|
||||
[Export] public Resource Dialogue { get; set; }
|
||||
|
||||
public void InitializeDungeon()
|
||||
{
|
||||
NPCBox.AreaEntered += NPCBox_AreaEntered;
|
||||
NPCBox.AreaExited += NPCBox_AreaExited;
|
||||
GameRepo.SetPlayerGlobalPosition(PlayerSpawnPoint.GlobalPosition);
|
||||
}
|
||||
|
||||
private void NPCBox_AreaExited(Area3D area)
|
||||
{
|
||||
GameRepo.IsWithinDialogueSpace = false;
|
||||
}
|
||||
|
||||
private void NPCBox_AreaEntered(Area3D area)
|
||||
{
|
||||
GameRepo.IsWithinDialogueSpace = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,24 +12,37 @@ public partial class Npc : Node3D
|
||||
|
||||
[Node] public Area3D DialogueZone { get; set; } = default!;
|
||||
|
||||
[Node] public Area3D Hitbox { get; set; } = default!;
|
||||
|
||||
[Export]
|
||||
public Godot.Collections.Array<Resource> DialogueOptions { get; set; } = default!;
|
||||
public Resource Dialogue { get; set; } = default!;
|
||||
|
||||
private bool _isInDialogueZone = false;
|
||||
|
||||
private Node _dialogueBalloon;
|
||||
private bool _isIntroductionComplete = false;
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
DialogueZone.BodyEntered += DialogueZone_BodyEntered;
|
||||
DialogueZone.BodyExited += DialogueZone_BodyExited;
|
||||
Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
|
||||
}
|
||||
|
||||
private void Hitbox_AreaEntered(Area3D area)
|
||||
{
|
||||
DialogueController.ShowDialogue(Dialogue, "hit");
|
||||
}
|
||||
|
||||
private void Hitbox_BodyEntered(Node body)
|
||||
{
|
||||
DialogueController.ShowDialogue(Dialogue, "hit");
|
||||
}
|
||||
|
||||
private void DialogueZone_BodyExited(Node3D body)
|
||||
{
|
||||
_isInDialogueZone = false;
|
||||
if (_dialogueBalloon != null)
|
||||
_dialogueBalloon.QueueFree();
|
||||
DialogueController.Interrupt();
|
||||
}
|
||||
|
||||
private void DialogueZone_BodyEntered(Node3D body)
|
||||
@@ -40,6 +53,14 @@ public partial class Npc : Node3D
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (Input.IsActionJustPressed(GameInputs.Throw) && _isInDialogueZone)
|
||||
_dialogueBalloon = DialogueManager.ShowDialogueBalloonScene(DialogueController.DialogueBalloon, DialogueOptions.First(), "introduction");
|
||||
{
|
||||
if (_isIntroductionComplete)
|
||||
DialogueController.ShowDialogue(Dialogue, "general");
|
||||
else
|
||||
{
|
||||
DialogueController.ShowDialogue(Dialogue, "introduction");
|
||||
_isIntroductionComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://d4l4qutp8x40c"]
|
||||
[gd_scene load_steps=7 format=3 uid="uid://d4l4qutp8x40c"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://t7fbfplu2js7" path="res://src/npc/rat/ROYAL_RAT_PRINCEP.png" id="1_0r0wh"]
|
||||
[ext_resource type="Script" path="res://src/npc/Npc.cs" id="1_cpdf2"]
|
||||
[ext_resource type="Resource" uid="uid://cf7ycgdiihyh" path="res://src/npc/rat/ratdialogue.dialogue" id="2_uo38w"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_wfhgc"]
|
||||
radius = 2.5
|
||||
radius = 3.0
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_kg3qv"]
|
||||
radius = 0.837722
|
||||
height = 2.8375
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_nwuwj"]
|
||||
height = 2.24425
|
||||
radius = 1.00578
|
||||
|
||||
[node name="NPC" type="Node3D"]
|
||||
script = ExtResource("1_cpdf2")
|
||||
DialogueOptions = Array[Resource]([ExtResource("2_uo38w")])
|
||||
Dialogue = ExtResource("2_uo38w")
|
||||
|
||||
[node name="Sprite" type="Sprite3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
@@ -28,3 +36,19 @@ collision_mask = 2
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="DialogueZone"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00131226, 0, -0.00723076)
|
||||
shape = SubResource("CylinderShape3D_wfhgc")
|
||||
|
||||
[node name="Collision" type="RigidBody3D" parent="."]
|
||||
collision_mask = 0
|
||||
gravity_scale = 0.0
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.169075, 0, 0)
|
||||
shape = SubResource("CapsuleShape3D_kg3qv")
|
||||
|
||||
[node name="Hitbox" type="Area3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
collision_layer = 16
|
||||
collision_mask = 16
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"]
|
||||
shape = SubResource("CylinderShape3D_nwuwj")
|
||||
|
||||
@@ -8,3 +8,41 @@ Royal Rat Princep, Tekohcyo: Alas, the tower has rejected me, so here I reside.
|
||||
Unable to return home, but unable to climb it.
|
||||
|
||||
=> END
|
||||
|
||||
~ general
|
||||
% => B1
|
||||
% => B2
|
||||
% => B3
|
||||
% => B4
|
||||
% => B5
|
||||
=> END
|
||||
|
||||
~ B1
|
||||
Royal Rat Princep, Tekohcyo: It became too much to bear, imagining what secrets they spoke within the walls.
|
||||
The Goddess could change their hearts…
|
||||
At least, it is what I dreamed of.
|
||||
=> END
|
||||
|
||||
~ B2
|
||||
Royal Rat Princep, Tekohcyo: I knew the Tower would save me. When it showed itself to me, I felt a hope I hadn’t felt since I was a child.
|
||||
Though it must have been a false hope in those days. What despairs did I possibly have to define that hope by?
|
||||
I was surrounded by love and play. The ideal environment for all living creatures.
|
||||
=> END
|
||||
|
||||
~ B3
|
||||
Royal Rat Princep, Tekohcyo: Though I am fearful of what my resolve could lead me to, I have yet to step foot in the audience chamber at the top.
|
||||
The thousand hands of the tower have always placed me gently where I fell, where I wished, and where I appear before you...
|
||||
=> END
|
||||
|
||||
~ B4
|
||||
Royal Rat Princep, Tekohcyo: Is it my own heart that needs to change, or the subjects around me?
|
||||
=> END
|
||||
|
||||
~ B5
|
||||
Royal Rat Princep, Tekohcyo: The walls of the Tower may deceive you.
|
||||
=> END
|
||||
|
||||
~ hit
|
||||
Royal Rat Princep, Tekohcyo: I suppose in the end, any end is, relievingly, an end.
|
||||
=> END
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ namespace GameJamDungeon
|
||||
})
|
||||
.Handle((in PlayerLogic.Output.ThrowItem output) =>
|
||||
{
|
||||
ThrowItem();
|
||||
});
|
||||
|
||||
this.Provide();
|
||||
|
||||
@@ -107,15 +107,14 @@ theme = SubResource("Theme_qq3yp")
|
||||
|
||||
[node name="Dialogue" type="MarginContainer" parent="Balloon"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 21.0
|
||||
offset_top = 897.0
|
||||
offset_right = 21.0
|
||||
offset_bottom = 897.0
|
||||
offset_top = -124.0
|
||||
offset_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Balloon/Dialogue"]
|
||||
layout_mode = 2
|
||||
@@ -147,7 +146,6 @@ theme_override_styles/fill = SubResource("StyleBoxEmpty_vtj1a")
|
||||
theme_override_styles/background = SubResource("StyleBoxEmpty_c2c5i")
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_wv0ko")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_dboi3")
|
||||
text = "Dialogue..."
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="Balloon"]
|
||||
layout_mode = 1
|
||||
|
||||
Reference in New Issue
Block a user