# HG changeset patch # User Alys Brooks # Date 2021-08-04 19:59:26 # Node ID 23e132ba3fe09877370b008b25010cef529951bd # Parent 7a38966b5d95ff1e2f2f6de92d436137eeb775ba Actually render dialog from Ink. diff --git a/SpriteFontPlus.FNA.Core.csproj b/SpriteFontPlus.FNA.Core.csproj --- a/SpriteFontPlus.FNA.Core.csproj +++ b/SpriteFontPlus.FNA.Core.csproj @@ -33,4 +33,8 @@ + + + + diff --git a/isometric-park-fna/Components/Dialog.cs b/isometric-park-fna/Components/Dialog.cs --- a/isometric-park-fna/Components/Dialog.cs +++ b/isometric-park-fna/Components/Dialog.cs @@ -1,4 +1,4 @@ - +using System.Collections.Generic; using Microsoft.Xna.Framework; @@ -9,7 +9,11 @@ namespace isometricparkfna.Components { - public struct DialogComponent : IComponent { + public struct DialogComponent : IComponent/*, IHasEntity*/ { public Node Dialog; + public string CurrentDialog; + // public Entity Entity {get; set;} + public string CurrentSpeaker; + public List Options; } } diff --git a/isometric-park-fna/Content/dialog.json b/isometric-park-fna/Content/dialog.json --- a/isometric-park-fna/Content/dialog.json +++ b/isometric-park-fna/Content/dialog.json @@ -1,1 +1,1 @@ -{"inkVersion":20,"root":[[{"->":"IntroGovernor"},["done",{"#f":5,"#n":"g-0"}],null],"done",{"Once":[["^Once upon a time...","\n",["ev",{"^->":"Once.0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^There were two choices.",{"->":"$r","var":true},null]}],["ev",{"^->":"Once.0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^There were four lines of content.",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"Once.0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"c-1":["ev",{"^->":"Once.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"g-0":["^They lived happily ever after.","\n","end",{"#f":5}]}],{"#f":1}],"IntroGovernor":[["^Governor: Welcome to your new park, director! You can use the mouse or arrow keys to move around, and the plus and minus keys to zoom in and out. B opens the budget and F lets you adjust Forest Policy.","\n","ev","str","^Okay","/str","/ev",{"*":".^.c-0","flg":20},{"c-0":["\n","^Governor: Make sure that you keep visitors happy and the budget in the black! You're currently getting an annual grant out of my budget—it'd sure be nice if you park were self-sufficient so we could drop that expense!","\n",["ev","str","^And I need to keep the forest healthy, too, right?","/str","/ev",{"*":".^.c-0","flg":20},"ev","str","^Sounds good!","/str","/ev",{"*":".^.c-1","flg":20},{"c-0":["^ ","\n","ev",{"VAR?":"GovernorOpinion"},1,"-","/ev",{"VAR=":"GovernorOpinion","re":true},"^Governor: Ummm, yeah","\n",[["ev",{"^->":"IntroGovernor.0.c-0.3.c-0.10.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^...",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"IntroGovernor.0.c-0.3.c-0.10.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"\n","end",{"#f":5}]}],{"#f":5}],"c-1":["\n","^Governor: I'll check in soon.","\n","end",{"#f":5}]}],{"#f":5}]}],{"#f":1}],"global decl":["ev",0,{"VAR=":"GovernorOpinion"},"/ev","end",null],"#f":1}],"listDefs":{}} \ No newline at end of file +{"inkVersion":20,"root":[[{"->":"IntroGovernor"},["done",{"#f":5,"#n":"g-0"}],null],"done",{"Once":[["^Once upon a time...","\n",["ev",{"^->":"Once.0.2.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^There were two choices.",{"->":"$r","var":true},null]}],["ev",{"^->":"Once.0.3.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-1","flg":18},{"s":["^There were four lines of content.",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"Once.0.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.2.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"c-1":["ev",{"^->":"Once.0.c-1.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.3.s"},[{"#n":"$r2"}],"\n",{"->":".^.^.g-0"},{"#f":5}],"g-0":["^They lived happily ever after.","\n","end",{"#f":5}]}],{"#f":1}],"IntroGovernor":[["^Governor: Welcome to your new park, director! You can use the mouse or arrow keys to move around, and the plus and minus keys to zoom in and out. B opens the budget and F lets you adjust Forest Policy.","\n","ev","str","^Okay","/str","/ev",{"*":".^.c-0","flg":20},{"c-0":["\n","^Governor: Make sure that you keep visitors happy and the budget in the black! You're currently getting an annual grant out of my budget—it'd sure be nice if you park were self-sufficient so we could drop that expense!","\n",["ev","str","^And I need to keep the forest healthy, too, right?","/str","/ev",{"*":".^.c-0","flg":20},"ev","str","^Sounds good!","/str","/ev",{"*":".^.c-1","flg":20},{"c-0":["^ ","\n","ev",{"VAR?":"GovernorOpinion"},1,"-","/ev",{"VAR=":"GovernorOpinion","re":true},"^Governor: Ummm, yeah","\n",[["ev",{"^->":"IntroGovernor.0.c-0.3.c-0.10.0.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","/ev",{"*":".^.^.c-0","flg":18},{"s":["^...",{"->":"$r","var":true},null]}],{"c-0":["ev",{"^->":"IntroGovernor.0.c-0.3.c-0.10.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.0.s"},[{"#n":"$r2"}],"\n","end",{"#f":5}]}],{"#f":5}],"c-1":["\n","ev",{"VAR?":"GovernorOpinion"},1,"+","/ev",{"VAR=":"GovernorOpinion","re":true},"^Governor: I'll check in soon.","\n","end",{"#f":5}]}],{"#f":5}]}],{"#f":1}],"global decl":["ev",0,{"VAR=":"GovernorOpinion"},"/ev","end",null],"#f":1}],"listDefs":{}} \ No newline at end of file diff --git a/isometric-park-fna/Engines/ImGuiWindowBridgeEngine.cs b/isometric-park-fna/Engines/ImGuiWindowBridgeEngine.cs --- a/isometric-park-fna/Engines/ImGuiWindowBridgeEngine.cs +++ b/isometric-park-fna/Engines/ImGuiWindowBridgeEngine.cs @@ -22,7 +22,8 @@ typeof(SetTrespassingPolicyMessage), typeof(SpawnGameMessage), typeof(SetTextVariableMessage), - typeof(SetDialogMessage))] + typeof(SetDialogMessage), + typeof(DialogChoiceMessage))] [Reads(typeof(VisibilityComponent), typeof(WindowTypeComponent), typeof(TrespassingPolicyComponent) @@ -44,6 +45,7 @@ public List spawnGameMessages; public List setTextVariableMessages; public List setDialogMessages; + public List dialogChoiceMessages; bool showBudget {get;} bool showForest {get;} @@ -73,6 +75,7 @@ this.spawnGameMessages = new List(); this.setTextVariableMessages = new List(); this.setDialogMessages = new List(); + this.dialogChoiceMessages = new List(); this.windowStatuses = new Dictionary(); @@ -157,6 +160,10 @@ { SendMessage(message); } + foreach (var message in this.dialogChoiceMessages) + { + SendMessage(message); + } @@ -183,6 +190,7 @@ this.spawnGameMessages.Clear(); this.setTextVariableMessages.Clear(); this.setDialogMessages.Clear(); + this.dialogChoiceMessages.Clear(); } } } diff --git a/isometric-park-fna/Engines/Spawners/DialogSpawner.cs b/isometric-park-fna/Engines/Spawners/DialogSpawner.cs new file mode 100644 --- /dev/null +++ b/isometric-park-fna/Engines/Spawners/DialogSpawner.cs @@ -0,0 +1,62 @@ + + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; + +using Microsoft.Xna.Framework; + +using isometricparkfna.Messages; +using isometricparkfna.Components; +using static isometricparkfna.CellMap; +using isometricparkfna.UI; + +using Encompass; +using TraceryNet; +using Ink.Runtime; + + +namespace isometricparkfna.Spawners { + + [Receives(typeof(SpawnDialogMessage))] + class DialogSpawner : Spawner + + { + Story Story; + Grammar Grammar; + + public DialogSpawner(Story story, Grammar grammar) + { + this.Story = story; + this.Grammar = grammar; + } + + protected override void Spawn(in SpawnDialogMessage message) + { + //Jump to the specified part of the story: + Story.ChoosePathString(message.Path); + + var newDialog = CreateEntity(); + var continuation = this.Story.ContinueMaximally(); + + var parts = Regex.Split(continuation, ":", 0); + var speaker = (parts.Length == 2) ? parts[0] : ""; + var dialog = (parts.Length == 2) ? parts[1] : continuation; + + AddComponent(newDialog, new DialogComponent { + CurrentDialog = dialog, + CurrentSpeaker = speaker, + Options = this.Story.currentChoices + .Select(option => option.text) + .ToList()}); + + AddComponent(newDialog, new WindowTypeComponent { + type = Window.Dialog}); + AddComponent(newDialog, + new VisibilityComponent{ visible = true}); + + Logging.Success("Spawned new dialog."); + } + } +} diff --git a/isometric-park-fna/Engines/Spawners/GameSpawner.cs b/isometric-park-fna/Engines/Spawners/GameSpawner.cs --- a/isometric-park-fna/Engines/Spawners/GameSpawner.cs +++ b/isometric-park-fna/Engines/Spawners/GameSpawner.cs @@ -17,10 +17,9 @@ [Receives(typeof(SpawnGameMessage))] [Sends(typeof(SpawnContractMessage), typeof(SpawnOrganizationtMessage), - typeof(ToggleWindowMessage))] - [Writes(typeof(WindowTypeComponent) - //, typeof(DialogComponent) - )] + typeof(ToggleWindowMessage), + typeof(SpawnDialogMessage))] + [Writes(typeof(WindowTypeComponent))] class GameSpawner : Spawner { @@ -110,6 +109,7 @@ type = OrganizationType.Cooperative }); #endregion #region dialog + /* // this.game.enqueueDialog(DialogTrees.flatten(DialogTrees.testTree, this.grammar)); // @@ -139,6 +139,9 @@ new VisibilityComponent{ visible = true}); +*/ + SendMessage(new SpawnDialogMessage { Path = "IntroGovernor"}); + // SendMessage(new SpawnDialogMessage { Path = "Once"}); #endregion this.simulation.Subsidy = message.Difficulty switch { DifficultyLevel.Hard => 0M, diff --git a/isometric-park-fna/Engines/UIEngine.cs b/isometric-park-fna/Engines/UIEngine.cs --- a/isometric-park-fna/Engines/UIEngine.cs +++ b/isometric-park-fna/Engines/UIEngine.cs @@ -1,6 +1,10 @@ +using System.Text.RegularExpressions; +using System.Linq; + using Microsoft.Xna.Framework.Input; using Encompass; +using Ink.Runtime; using isometricparkfna.Messages; using isometricparkfna.Components; @@ -13,7 +17,8 @@ typeof(ToggleVisibilityMessage), typeof(SetWindowVisibilityMessage), typeof(SelectMessage), - typeof(SetDialogMessage))] + typeof(SetDialogMessage), + typeof(DialogChoiceMessage))] [Reads(typeof(WindowTypeComponent), typeof(VisibilityComponent), typeof(SelectedComponent))] @@ -22,6 +27,12 @@ typeof(DialogComponent))] class UIEngine : Engine { + public Story Story; + + public UIEngine(Story story) + { + this.Story = story; + } public override void Update(double dt) { @@ -99,6 +110,34 @@ } } + + foreach (ref readonly var choiceMessage in ReadMessages()) + { + + if (Story.currentChoices.Count > 0) + { + //Advance the story + Story.ChooseChoiceIndex(choiceMessage.Choice); + + //Update the dialog component with the new speaker, dialog, and options: + var continuation = this.Story.ContinueMaximally(); + var parts = Regex.Split(continuation, ":", 0); + var speaker = (parts.Length == 2) ? parts[0] : ""; + var dialog = (parts.Length == 2) ? parts[1] : continuation; + + SetComponent(choiceMessage.Entity, new DialogComponent { + CurrentDialog = dialog, + CurrentSpeaker = speaker, + Options = this.Story.currentChoices + .Select(option => option.text) + .ToList()}); + } + else + { + Destroy(choiceMessage.Entity); + } + + } } } } diff --git a/isometric-park-fna/FNAGame.cs b/isometric-park-fna/FNAGame.cs --- a/isometric-park-fna/FNAGame.cs +++ b/isometric-park-fna/FNAGame.cs @@ -87,6 +87,8 @@ private bool showInitial; int messageIndex; + public Story Story; + //buggy private static bool enableCulling = false; @@ -242,12 +244,12 @@ //Has to happen after Grammar initialization. NewGameWindow.Initialize(this.grammar); - var _inkStory = new Story(File.ReadAllText(@"Content/dialog.json")); + this.Story = new Story(File.ReadAllText(@"Content/dialog.json")); - Logging.Debug(_inkStory.ContinueMaximally()); + // Logging.Debug(this.Story.ContinueMaximally()); WorldBuilder.AddEngine(new InputEngine(Menu.MENU_BAR_HEIGHT, this.camera, gdm)); - WorldBuilder.AddEngine(new UIEngine()); + WorldBuilder.AddEngine(new UIEngine(this.Story)); var gameBridgeEngine = new GameBridgeEngine(this); @@ -262,6 +264,7 @@ WorldBuilder.AddEngine(new ContractSpawner(simulation.map.MapWidth, simulation.map.MapHeight, this.simulation, this.grammar)); WorldBuilder.AddEngine(new GameSpawner(this.simulation, this, this.grammar)); WorldBuilder.AddEngine(new OrganizationSpawner(this.simulation, this.grammar)); + WorldBuilder.AddEngine(new DialogSpawner(this.Story, this.grammar)); WorldBuilder.AddEngine(new PolicyEngine()); WorldBuilder.AddEngine(new TraceryBridgeEngine(this.grammar)); diff --git a/isometric-park-fna/Messages/DialogChoiceMessage.cs b/isometric-park-fna/Messages/DialogChoiceMessage.cs new file mode 100644 --- /dev/null +++ b/isometric-park-fna/Messages/DialogChoiceMessage.cs @@ -0,0 +1,17 @@ + +using Encompass; + +using isometricparkfna.Utils; +using isometricparkfna.UI; + +namespace isometricparkfna.Messages +{ + + //You must specify both or you get 0 for a window, which is the default window :( + public struct DialogChoiceMessage : IMessage, IHasEntity + { + + public Entity Entity { set; get; } + public int Choice; + } +} diff --git a/isometric-park-fna/Messages/SpawnDialog.cs b/isometric-park-fna/Messages/SpawnDialog.cs new file mode 100644 --- /dev/null +++ b/isometric-park-fna/Messages/SpawnDialog.cs @@ -0,0 +1,15 @@ + + +using Microsoft.Xna.Framework; +using Encompass; + +using isometricparkfna.Spawners; + +#nullable enable + +namespace isometricparkfna.Messages { + public struct SpawnDialogMessage : IMessage + { + public string Path; + } +} diff --git a/isometric-park-fna/Renderers/ImGuiWindowRenderer.cs b/isometric-park-fna/Renderers/ImGuiWindowRenderer.cs --- a/isometric-park-fna/Renderers/ImGuiWindowRenderer.cs +++ b/isometric-park-fna/Renderers/ImGuiWindowRenderer.cs @@ -69,7 +69,7 @@ var width = gdm.PreferredBackBufferWidth; Entity dialogEntity = default; - Node dialogNode = default; + DialogComponent dialogNode = default; @@ -119,7 +119,7 @@ break; case Window.Dialog: - var dialog = GetComponent(entity).Dialog; + var dialog = GetComponent(entity); if ((dialogEntity == null) || (dialogEntity.ID == 0) @@ -136,7 +136,7 @@ } } - if (dialogNode != null) + if (dialogNode.CurrentDialog != null) { var show = true; var paused = true; diff --git a/isometric-park-fna/UI/Dialog.cs b/isometric-park-fna/UI/Dialog.cs --- a/isometric-park-fna/UI/Dialog.cs +++ b/isometric-park-fna/UI/Dialog.cs @@ -8,6 +8,7 @@ using Encompass; using isometricparkfna.Engines; +using isometricparkfna.Components; using isometricparkfna.Messages; #nullable enable @@ -112,9 +113,9 @@ public static bool hadFocus = false; public static Node RenderDialog(Entity entity, - ImGuiWindowBridgeEngine bridgeEngine, ref bool show, ref bool paused, ImFontPtr font, Node currentNode) + ImGuiWindowBridgeEngine bridgeEngine, ref bool show, ref bool paused, ImFontPtr font, DialogComponent dialogComponent) { - Node new_child = currentNode; + Node new_child = default; if (show) { ImGui.PushFont(font); @@ -127,7 +128,8 @@ { ImGui.PushStyleColor(ImGuiCol.Text, StyleSets.white); } - ImGui.Begin(currentNode.data.speaker, ref show, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings); + + ImGui.Begin(dialogComponent.CurrentSpeaker, ref show, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings); if (DialogInterface.hadFocus) { ImGui.PopStyleColor(); @@ -135,21 +137,25 @@ DialogInterface.hadFocus = ImGui.IsWindowFocused(); - if (currentNode.data.response != null) + if (dialogComponent.CurrentDialog != null) { - string messageText = currentNode.data.response; + string messageText = dialogComponent.CurrentDialog; ImGui.TextWrapped(messageText); } - if ((currentNode.children != null) && currentNode.children.Length > 0) + if ((dialogComponent.Options != null) && dialogComponent.Options.Count > 0) { - //Filter out nulls - foreach ((Node child, string choice) in currentNode.children.Select((child) => (child, child.data.choice)).Where( (pair, choice) => pair.choice != null)) + // foreach (var choice in dialogComponent.Options) + for(int i = 0; i < dialogComponent.Options.Count; i++) { - string buttonText = choice; + string buttonText = dialogComponent.Options[i]; if (ImGui.Button(buttonText)) { - new_child = child; + bridgeEngine.dialogChoiceMessages.Add(new DialogChoiceMessage { + + Entity = entity, + Choice = i }); + } } } @@ -159,14 +165,14 @@ { show = false; paused = false; - bridgeEngine.setDialogMessages.Add(new SetDialogMessage { + bridgeEngine.dialogChoiceMessages.Add(new DialogChoiceMessage { Entity = entity, - newOption = null }); + Choice = -1 }); } } - if (currentNode.data.response == null) + if (dialogComponent.CurrentDialog == null) { show = false; paused = false; @@ -177,10 +183,11 @@ StyleSets.defaultSet.pop(); ImGui.PopFont(); } - bridgeEngine.setDialogMessages.Add(new SetDialogMessage { - - Entity = entity, - newOption = new_child }); + // bridgeEngine.setDialogMessages.Add(new SetDialogMessage { + // + // Entity = entity, + // newOption = new_child }); + return new_child; } }