From 8c07d73fa65b8ab89ecc5f6c81d48852135e82d2 Mon Sep 17 00:00:00 2001 From: Ash Blue Date: Wed, 24 Apr 2024 14:18:51 -0700 Subject: [PATCH] Merge pull request #45 from ashblue/feature/minor-refactoring New note node, send message API, and custom node creation preview --- CHANGELOG.md | 1 + CHANGELOG.md.meta | 7 + Editor.meta | 8 + Editor/Attributes.meta | 3 + Editor/Attributes/NodeTypeAttribute.cs | 11 + Editor/Attributes/NodeTypeAttribute.cs.meta | 3 + Editor/CreateDialogueGraph.cs | 59 +++ Editor/CreateDialogueGraph.cs.meta | 11 + Editor/Inspectors.meta | 3 + Editor/Inspectors/DialogueGraphInspector.cs | 71 ++++ .../Inspectors/DialogueGraphInspector.cs.meta | 3 + Editor/Inspectors/NodeDataBase.meta | 3 + .../NodeDataBase/NodeDataBaseEditor.cs | 71 ++++ .../NodeDataBase/NodeDataBaseEditor.cs.meta | 3 + .../NodeDataBase/SortableLists.meta | 3 + .../SortableLists/ActionsSortableList.cs | 28 ++ .../SortableLists/ActionsSortableList.cs.meta | 3 + .../SortableLists/ConditionSortableList.cs | 28 ++ .../ConditionSortableList.cs.meta | 3 + .../SortableLists/NestedDataCrud.cs | 85 ++++ .../SortableLists/NestedDataCrud.cs.meta | 3 + .../ScriptableObjectListPrinter.cs | 65 +++ .../ScriptableObjectListPrinter.cs.meta | 3 + .../NodeDataBase/SortableLists/TypesToMenu.cs | 41 ++ .../SortableLists/TypesToMenu.cs.meta | 3 + Editor/Inspectors/SortableListBase.cs | 37 ++ Editor/Inspectors/SortableListBase.cs.meta | 3 + Editor/NodeEditors.meta | 3 + Editor/NodeEditors/Base.meta | 3 + Editor/NodeEditors/Base/HeaderTextStyle.cs | 23 + .../NodeEditors/Base/HeaderTextStyle.cs.meta | 3 + Editor/NodeEditors/Base/NodeEditorBase.cs | 183 ++++++++ .../NodeEditors/Base/NodeEditorBase.cs.meta | 3 + .../Base/NodeEditorBaseConnections.cs | 84 ++++ .../Base/NodeEditorBaseConnections.cs.meta | 3 + Editor/NodeEditors/Base/NodeStyles.cs | 24 ++ Editor/NodeEditors/Base/NodeStyles.cs.meta | 3 + Editor/NodeEditors/ChoiceEditor.cs | 42 ++ Editor/NodeEditors/ChoiceEditor.cs.meta | 3 + Editor/NodeEditors/Connections.meta | 3 + Editor/NodeEditors/Connections/Connection.cs | 124 ++++++ .../Connections/Connection.cs.meta | 3 + .../Connections/ConnectionCurves.cs | 34 ++ .../Connections/ConnectionCurves.cs.meta | 3 + .../Connections/ConnectionLinks.cs | 77 ++++ .../Connections/ConnectionLinks.cs.meta | 3 + .../Connections/ConnectionParents.cs | 22 + .../Connections/ConnectionParents.cs.meta | 3 + .../NodeEditors/Connections/ConnectionType.cs | 6 + .../Connections/ConnectionType.cs.meta | 3 + Editor/NodeEditors/Dialogue.meta | 3 + .../NodeEditors/Dialogue/ChoiceCollection.cs | 222 ++++++++++ .../Dialogue/ChoiceCollection.cs.meta | 3 + Editor/NodeEditors/Dialogue/DialogueEditor.cs | 44 ++ .../Dialogue/DialogueEditor.cs.meta | 3 + Editor/NodeEditors/HubEditor.cs | 9 + Editor/NodeEditors/HubEditor.cs.meta | 3 + Editor/NodeEditors/NoteEditor.cs | 18 + Editor/NodeEditors/NoteEditor.cs.meta | 3 + Editor/NodeEditors/PlayGraphEditor.cs | 17 + Editor/NodeEditors/PlayGraphEditor.cs.meta | 3 + Editor/NodeEditors/RootEditor.cs | 9 + Editor/NodeEditors/RootEditor.cs.meta | 3 + Editor/Resources.meta | 3 + Editor/Resources/dot.png | Bin 0 -> 18297 bytes Editor/Resources/dot.png.meta | 90 ++++ Editor/Utilities.meta | 3 + Editor/Utilities/NodeAssemblies.cs | 68 +++ Editor/Utilities/NodeAssemblies.cs.meta | 3 + Editor/Utilities/NodeBoxStyle.cs | 56 +++ Editor/Utilities/NodeBoxStyle.cs.meta | 3 + Editor/Utilities/RectCleaner.cs | 21 + Editor/Utilities/RectCleaner.cs.meta | 3 + Editor/Utilities/ThemeUtility.cs | 7 + Editor/Utilities/ThemeUtility.cs.meta | 3 + Editor/Windows.meta | 3 + Editor/Windows/DialogueWindow.cs | 126 ++++++ Editor/Windows/DialogueWindow.cs.meta | 3 + Editor/Windows/DialogueWindowRestore.cs | 39 ++ Editor/Windows/DialogueWindowRestore.cs.meta | 3 + Editor/Windows/DialogueWindowUndoRedo.cs | 32 ++ Editor/Windows/DialogueWindowUndoRedo.cs.meta | 3 + Editor/Windows/FindReplaceWindow.meta | 3 + .../FindReplaceWindow/ChoiceSearchResult.cs | 31 ++ .../ChoiceSearchResult.cs.meta | 3 + .../FindReplaceWindow/DialogueSearchResult.cs | 31 ++ .../DialogueSearchResult.cs.meta | 3 + .../FindReplaceWindow/FindReplaceWindow.cs | 39 ++ .../FindReplaceWindow.cs.meta | 3 + Editor/Windows/FluidDialogueSettings.cs | 54 +++ Editor/Windows/FluidDialogueSettings.cs.meta | 3 + Editor/Windows/GraphCrud.cs | 102 +++++ Editor/Windows/GraphCrud.cs.meta | 3 + Editor/Windows/UserInput.meta | 3 + Editor/Windows/UserInput/DelayedMenu.cs | 16 + Editor/Windows/UserInput/DelayedMenu.cs.meta | 3 + Editor/Windows/UserInput/InputController.cs | 37 ++ .../Windows/UserInput/InputController.cs.meta | 3 + Editor/Windows/UserInput/LeftClickHandler.cs | 192 +++++++++ .../UserInput/LeftClickHandler.cs.meta | 3 + Editor/Windows/UserInput/NodeSelection.cs | 57 +++ .../Windows/UserInput/NodeSelection.cs.meta | 3 + Editor/Windows/UserInput/RightClickHandler.cs | 150 +++++++ .../UserInput/RightClickHandler.cs.meta | 3 + Editor/Windows/UserInput/ScrollManager.cs | 41 ++ .../Windows/UserInput/ScrollManager.cs.meta | 3 + Editor/com.fluid.dialogue.Editor.asmdef | 19 + Editor/com.fluid.dialogue.Editor.asmdef.meta | 7 + LICENSE.md | 1 + LICENSE.md.meta | 7 + README.md | 1 + README.md.meta | 7 + Runtime.meta | 8 + Runtime/Actions.meta | 3 + Runtime/Actions/ActionDataBase.cs | 22 + Runtime/Actions/ActionDataBase.cs.meta | 3 + Runtime/Actions/ActionRuntime.cs | 77 ++++ Runtime/Actions/ActionRuntime.cs.meta | 3 + Runtime/Actions/ActionStatus.cs | 6 + Runtime/Actions/ActionStatus.cs.meta | 3 + Runtime/Actions/IAction.cs | 6 + Runtime/Actions/IAction.cs.meta | 3 + Runtime/Actions/Libraries.meta | 8 + Runtime/Actions/Libraries/Databases.meta | 8 + .../Actions/Libraries/Databases/Globals.meta | 3 + .../Libraries/Databases/Globals/Actions.meta | 3 + .../Globals/Actions/SetGlobalBool.cs | 16 + .../Globals/Actions/SetGlobalBool.cs.meta | 3 + .../Globals/Actions/SetGlobalFloat.cs | 16 + .../Globals/Actions/SetGlobalFloat.cs.meta | 3 + .../Databases/Globals/Actions/SetGlobalInt.cs | 16 + .../Globals/Actions/SetGlobalInt.cs.meta | 3 + .../Globals/Actions/SetGlobalString.cs | 16 + .../Globals/Actions/SetGlobalString.cs.meta | 3 + .../Databases/Globals/Conditions.meta | 3 + .../Globals/Conditions/IsGlobalBool.cs | 11 + .../Globals/Conditions/IsGlobalBool.cs.meta | 3 + .../Globals/Conditions/IsGlobalFloat.cs | 11 + .../Globals/Conditions/IsGlobalFloat.cs.meta | 3 + .../Globals/Conditions/IsGlobalInt.cs | 11 + .../Globals/Conditions/IsGlobalInt.cs.meta | 3 + .../Globals/Conditions/IsGlobalString.cs | 11 + .../Globals/Conditions/IsGlobalString.cs.meta | 3 + .../Actions/Libraries/Databases/Locals.meta | 8 + .../Libraries/Databases/Locals/Actions.meta | 3 + .../Databases/Locals/Actions/SetLocalBool.cs | 16 + .../Locals/Actions/SetLocalBool.cs.meta | 11 + .../Databases/Locals/Actions/SetLocalFloat.cs | 16 + .../Locals/Actions/SetLocalFloat.cs.meta | 3 + .../Databases/Locals/Actions/SetLocalInt.cs | 16 + .../Locals/Actions/SetLocalInt.cs.meta | 3 + .../Locals/Actions/SetLocalString.cs | 16 + .../Locals/Actions/SetLocalString.cs.meta | 3 + .../Locals/Actions/SetLocalVariableBase.cs | 37 ++ .../Actions/SetLocalVariableBase.cs.meta | 3 + .../Databases/Locals/Conditions.meta | 3 + .../Locals/Conditions/IsLocalBool.meta | 3 + .../Conditions/IsLocalBool/IsBoolBase.cs | 62 +++ .../Conditions/IsLocalBool/IsBoolBase.cs.meta | 3 + .../Conditions/IsLocalBool/IsLocalBool.cs | 10 + .../IsLocalBool/IsLocalBool.cs.meta | 3 + .../Locals/Conditions/IsLocalFloat.meta | 3 + .../Conditions/IsLocalFloat/IsFloatBase.cs | 61 +++ .../IsLocalFloat/IsFloatBase.cs.meta | 3 + .../Conditions/IsLocalFloat/IsLocalFloat.cs | 10 + .../IsLocalFloat/IsLocalFloat.cs.meta | 3 + .../Locals/Conditions/IsLocalInt.meta | 3 + .../Locals/Conditions/IsLocalInt/IsIntBase.cs | 59 +++ .../Conditions/IsLocalInt/IsIntBase.cs.meta | 3 + .../Conditions/IsLocalInt/IsLocalInt.cs | 10 + .../Conditions/IsLocalInt/IsLocalInt.cs.meta | 3 + .../Locals/Conditions/IsLocalString.meta | 3 + .../Conditions/IsLocalString/IsLocalString.cs | 10 + .../IsLocalString/IsLocalString.cs.meta | 3 + .../Conditions/IsLocalString/IsStringBase.cs | 62 +++ .../IsLocalString/IsStringBase.cs.meta | 3 + .../Locals/Conditions/NumberComparison.cs | 10 + .../Conditions/NumberComparison.cs.meta | 3 + Runtime/Actions/Libraries/GameObjects.meta | 3 + .../Libraries/GameObjects/Actions.meta | 3 + .../GameObjects/Actions/ActionSetActive.cs | 19 + .../Actions/ActionSetActive.cs.meta | 3 + .../Actions/GameObjectUtilities.cs | 50 +++ .../Actions/GameObjectUtilities.cs.meta | 3 + .../GameObjects/Actions/SendMessage.meta | 3 + .../Actions/SendMessage/ActionSendMessage.cs | 22 + .../SendMessage/ActionSendMessage.cs.meta | 3 + .../SendMessage/ActionSendMessageBool.cs | 25 ++ .../SendMessage/ActionSendMessageBool.cs.meta | 3 + .../SendMessage/ActionSendMessageFloat.cs | 25 ++ .../ActionSendMessageFloat.cs.meta | 3 + .../SendMessage/ActionSendMessageInt.cs | 25 ++ .../SendMessage/ActionSendMessageInt.cs.meta | 3 + .../SendMessage/ActionSendMessageString.cs | 25 ++ .../ActionSendMessageString.cs.meta | 3 + Runtime/Actors.meta | 3 + Runtime/Actors/ActorDefinition.cs | 15 + Runtime/Actors/ActorDefinition.cs.meta | 3 + Runtime/Actors/IActor.cs | 8 + Runtime/Actors/IActor.cs.meta | 3 + Runtime/Attributes.meta | 3 + Runtime/Attributes/CreateMenuAttribute.cs | 13 + .../Attributes/CreateMenuAttribute.cs.meta | 3 + Runtime/Choices.meta | 3 + Runtime/Choices/ChoiceData.cs | 52 +++ Runtime/Choices/ChoiceData.cs.meta | 3 + Runtime/Choices/ChoiceRuntime.cs | 31 ++ Runtime/Choices/ChoiceRuntime.cs.meta | 3 + Runtime/Choices/IChoice.cs | 10 + Runtime/Choices/IChoice.cs.meta | 3 + Runtime/Conditions.meta | 3 + Runtime/Conditions/ConditionDataBase.cs | 13 + Runtime/Conditions/ConditionDataBase.cs.meta | 3 + Runtime/Conditions/ConditionRuntime.cs | 33 ++ Runtime/Conditions/ConditionRuntime.cs.meta | 3 + Runtime/Conditions/ICondition.cs | 9 + Runtime/Conditions/ICondition.cs.meta | 3 + Runtime/DialogueController.meta | 3 + .../DialogueController/DialogueController.cs | 217 ++++++++++ .../DialogueController.cs.meta | 3 + Runtime/DialoguePlayback.cs | 149 +++++++ Runtime/DialoguePlayback.cs.meta | 11 + Runtime/Graphs.meta | 3 + Runtime/Graphs/DialogueGraph.cs | 34 ++ Runtime/Graphs/DialogueGraph.cs.meta | 3 + Runtime/Graphs/Events.meta | 3 + Runtime/Graphs/Events/DialogueEvents.cs | 16 + Runtime/Graphs/Events/DialogueEvents.cs.meta | 3 + Runtime/Graphs/Events/IDialogueEvents.cs | 21 + Runtime/Graphs/Events/IDialogueEvents.cs.meta | 3 + Runtime/Graphs/GraphRuntime.cs | 29 ++ Runtime/Graphs/GraphRuntime.cs.meta | 3 + Runtime/Graphs/IGraph.cs | 11 + Runtime/Graphs/IGraph.cs.meta | 3 + Runtime/IGetRuntime.cs | 7 + Runtime/IGetRuntime.cs.meta | 3 + Runtime/ISetup.cs | 5 + Runtime/ISetup.cs.meta | 3 + Runtime/IUniqueId.cs | 5 + Runtime/IUniqueId.cs.meta | 3 + Runtime/Nodes.meta | 3 + Runtime/Nodes/ChoiceHub.meta | 3 + Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs | 41 ++ Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs.meta | 3 + Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs | 18 + .../Nodes/ChoiceHub/NodeChoiceHubData.cs.meta | 3 + Runtime/Nodes/Dialogue.meta | 3 + Runtime/Nodes/Dialogue/NodeDialogue.cs | 63 +++ Runtime/Nodes/Dialogue/NodeDialogue.cs.meta | 3 + Runtime/Nodes/Dialogue/NodeDialogueData.cs | 32 ++ .../Nodes/Dialogue/NodeDialogueData.cs.meta | 12 + Runtime/Nodes/Hub.meta | 3 + Runtime/Nodes/Hub/NodeHub.cs | 18 + Runtime/Nodes/Hub/NodeHub.cs.meta | 3 + Runtime/Nodes/Hub/NodeHubData.cs | 19 + Runtime/Nodes/Hub/NodeHubData.cs.meta | 3 + Runtime/Nodes/INode.cs | 21 + Runtime/Nodes/INode.cs.meta | 3 + Runtime/Nodes/Links.meta | 3 + Runtime/Nodes/Links/NodeLink.cs | 20 + Runtime/Nodes/Links/NodeLink.cs.meta | 3 + Runtime/Nodes/Links/NodeLinkData.cs | 17 + Runtime/Nodes/Links/NodeLinkData.cs.meta | 3 + Runtime/Nodes/NodeBase.cs | 60 +++ Runtime/Nodes/NodeBase.cs.meta | 3 + Runtime/Nodes/NodeDataBase.cs | 88 ++++ Runtime/Nodes/NodeDataBase.cs.meta | 3 + Runtime/Nodes/NodeDataChoiceBase.cs | 34 ++ Runtime/Nodes/NodeDataChoiceBase.cs.meta | 3 + Runtime/Nodes/NodeNestedDataBase.cs | 26 ++ Runtime/Nodes/NodeNestedDataBase.cs.meta | 3 + Runtime/Nodes/Note.meta | 3 + Runtime/Nodes/Note/NodeNoteData.cs | 25 ++ Runtime/Nodes/Note/NodeNoteData.cs.meta | 3 + Runtime/Nodes/PlayGraph.meta | 3 + Runtime/Nodes/PlayGraph/NodePlayGraph.cs | 28 ++ Runtime/Nodes/PlayGraph/NodePlayGraph.cs.meta | 3 + Runtime/Nodes/PlayGraph/NodePlayGraphData.cs | 24 ++ .../Nodes/PlayGraph/NodePlayGraphData.cs.meta | 3 + Runtime/Nodes/Root.meta | 3 + Runtime/Nodes/Root/NodeRoot.cs | 18 + Runtime/Nodes/Root/NodeRoot.cs.meta | 3 + Runtime/Nodes/Root/NodeRootData.cs | 19 + Runtime/Nodes/Root/NodeRootData.cs.meta | 3 + Runtime/com.fluid.dialogue.asmdef | 17 + Runtime/com.fluid.dialogue.asmdef.meta | 7 + Tests.meta | 8 + Tests/Editor.meta | 8 + Tests/Editor/Actions.meta | 3 + Tests/Editor/Actions/ActionRuntimeTest.cs | 154 +++++++ .../Editor/Actions/ActionRuntimeTest.cs.meta | 3 + Tests/Editor/Actions/Databases.meta | 3 + .../Databases/ConditionLocalBoolTest.cs | 52 +++ .../Databases/ConditionLocalBoolTest.cs.meta | 3 + .../Databases/ConditionLocalFloatTest.cs | 132 ++++++ .../Databases/ConditionLocalFloatTest.cs.meta | 3 + .../Databases/ConditionLocalIntTest.cs | 132 ++++++ .../Databases/ConditionLocalIntTest.cs.meta | 3 + .../Databases/ConditionLocalStringTest.cs | 54 +++ .../ConditionLocalStringTest.cs.meta | 3 + .../Actions/Databases/SetLocalBoolTest.cs | 22 + .../Databases/SetLocalBoolTest.cs.meta | 3 + Tests/Editor/Builders.meta | 3 + Tests/Editor/Builders/A.cs | 9 + Tests/Editor/Builders/A.cs.meta | 3 + Tests/Editor/Builders/ActionStubBuilder.cs | 20 + .../Editor/Builders/ActionStubBuilder.cs.meta | 3 + Tests/Editor/Builders/ChoiceStubBuilder.cs | 34 ++ .../Editor/Builders/ChoiceStubBuilder.cs.meta | 3 + .../Builders/DialogueGraphStubBuilder.cs | 44 ++ .../Builders/DialogueGraphStubBuilder.cs.meta | 3 + .../Builders/DialogueNodeStubBuilder.cs | 76 ++++ .../Builders/DialogueNodeStubBuilder.cs.meta | 3 + Tests/Editor/Builders/NodeDataStubBuilder.cs | 20 + .../Builders/NodeDataStubBuilder.cs.meta | 3 + Tests/Editor/ChoiceRuntimeTest.cs | 73 ++++ Tests/Editor/ChoiceRuntimeTest.cs.meta | 3 + Tests/Editor/ConditionRuntimeTest.cs | 49 +++ Tests/Editor/ConditionRuntimeTest.cs.meta | 3 + Tests/Editor/DialogueControllerTest.cs | 309 ++++++++++++++ Tests/Editor/DialogueControllerTest.cs.meta | 3 + Tests/Editor/DialoguePlaybackTest.cs | 392 ++++++++++++++++++ Tests/Editor/DialoguePlaybackTest.cs.meta | 3 + Tests/Editor/GraphRuntimeTest.cs | 45 ++ Tests/Editor/GraphRuntimeTest.cs.meta | 3 + Tests/Editor/Nodes.meta | 3 + Tests/Editor/Nodes/NodeChoiceHubTest.cs | 69 +++ Tests/Editor/Nodes/NodeChoiceHubTest.cs.meta | 3 + Tests/Editor/Nodes/NodeDialogueTest.cs | 316 ++++++++++++++ Tests/Editor/Nodes/NodeDialogueTest.cs.meta | 3 + Tests/Editor/Nodes/NodeHubTest.cs | 89 ++++ Tests/Editor/Nodes/NodeHubTest.cs.meta | 3 + Tests/Editor/Nodes/NodeLinkTest.cs | 66 +++ Tests/Editor/Nodes/NodeLinkTest.cs.meta | 3 + Tests/Editor/Nodes/NodePlayGraphTest.cs | 21 + Tests/Editor/Nodes/NodePlayGraphTest.cs.meta | 3 + Tests/Editor/Nodes/NodeRootTest.cs | 33 ++ Tests/Editor/Nodes/NodeRootTest.cs.meta | 3 + Tests/Editor/Utilities.meta | 3 + Tests/Editor/Utilities/RectCleanerTest.cs | 52 +++ .../Editor/Utilities/RectCleanerTest.cs.meta | 3 + .../com.fluid.dialogue.Editor.Tests.asmdef | 26 ++ ...om.fluid.dialogue.Editor.Tests.asmdef.meta | 7 + package.json | 13 + package.json.meta | 7 + 345 files changed, 7708 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 CHANGELOG.md.meta create mode 100644 Editor.meta create mode 100644 Editor/Attributes.meta create mode 100644 Editor/Attributes/NodeTypeAttribute.cs create mode 100644 Editor/Attributes/NodeTypeAttribute.cs.meta create mode 100644 Editor/CreateDialogueGraph.cs create mode 100644 Editor/CreateDialogueGraph.cs.meta create mode 100644 Editor/Inspectors.meta create mode 100644 Editor/Inspectors/DialogueGraphInspector.cs create mode 100644 Editor/Inspectors/DialogueGraphInspector.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase.meta create mode 100644 Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs create mode 100644 Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs.meta create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs create mode 100644 Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs.meta create mode 100644 Editor/Inspectors/SortableListBase.cs create mode 100644 Editor/Inspectors/SortableListBase.cs.meta create mode 100644 Editor/NodeEditors.meta create mode 100644 Editor/NodeEditors/Base.meta create mode 100644 Editor/NodeEditors/Base/HeaderTextStyle.cs create mode 100644 Editor/NodeEditors/Base/HeaderTextStyle.cs.meta create mode 100644 Editor/NodeEditors/Base/NodeEditorBase.cs create mode 100644 Editor/NodeEditors/Base/NodeEditorBase.cs.meta create mode 100644 Editor/NodeEditors/Base/NodeEditorBaseConnections.cs create mode 100644 Editor/NodeEditors/Base/NodeEditorBaseConnections.cs.meta create mode 100644 Editor/NodeEditors/Base/NodeStyles.cs create mode 100644 Editor/NodeEditors/Base/NodeStyles.cs.meta create mode 100644 Editor/NodeEditors/ChoiceEditor.cs create mode 100644 Editor/NodeEditors/ChoiceEditor.cs.meta create mode 100644 Editor/NodeEditors/Connections.meta create mode 100644 Editor/NodeEditors/Connections/Connection.cs create mode 100644 Editor/NodeEditors/Connections/Connection.cs.meta create mode 100644 Editor/NodeEditors/Connections/ConnectionCurves.cs create mode 100644 Editor/NodeEditors/Connections/ConnectionCurves.cs.meta create mode 100644 Editor/NodeEditors/Connections/ConnectionLinks.cs create mode 100644 Editor/NodeEditors/Connections/ConnectionLinks.cs.meta create mode 100644 Editor/NodeEditors/Connections/ConnectionParents.cs create mode 100644 Editor/NodeEditors/Connections/ConnectionParents.cs.meta create mode 100644 Editor/NodeEditors/Connections/ConnectionType.cs create mode 100644 Editor/NodeEditors/Connections/ConnectionType.cs.meta create mode 100644 Editor/NodeEditors/Dialogue.meta create mode 100644 Editor/NodeEditors/Dialogue/ChoiceCollection.cs create mode 100644 Editor/NodeEditors/Dialogue/ChoiceCollection.cs.meta create mode 100644 Editor/NodeEditors/Dialogue/DialogueEditor.cs create mode 100644 Editor/NodeEditors/Dialogue/DialogueEditor.cs.meta create mode 100644 Editor/NodeEditors/HubEditor.cs create mode 100644 Editor/NodeEditors/HubEditor.cs.meta create mode 100644 Editor/NodeEditors/NoteEditor.cs create mode 100644 Editor/NodeEditors/NoteEditor.cs.meta create mode 100644 Editor/NodeEditors/PlayGraphEditor.cs create mode 100644 Editor/NodeEditors/PlayGraphEditor.cs.meta create mode 100644 Editor/NodeEditors/RootEditor.cs create mode 100644 Editor/NodeEditors/RootEditor.cs.meta create mode 100644 Editor/Resources.meta create mode 100644 Editor/Resources/dot.png create mode 100644 Editor/Resources/dot.png.meta create mode 100644 Editor/Utilities.meta create mode 100644 Editor/Utilities/NodeAssemblies.cs create mode 100644 Editor/Utilities/NodeAssemblies.cs.meta create mode 100644 Editor/Utilities/NodeBoxStyle.cs create mode 100644 Editor/Utilities/NodeBoxStyle.cs.meta create mode 100644 Editor/Utilities/RectCleaner.cs create mode 100644 Editor/Utilities/RectCleaner.cs.meta create mode 100644 Editor/Utilities/ThemeUtility.cs create mode 100644 Editor/Utilities/ThemeUtility.cs.meta create mode 100644 Editor/Windows.meta create mode 100644 Editor/Windows/DialogueWindow.cs create mode 100644 Editor/Windows/DialogueWindow.cs.meta create mode 100644 Editor/Windows/DialogueWindowRestore.cs create mode 100644 Editor/Windows/DialogueWindowRestore.cs.meta create mode 100644 Editor/Windows/DialogueWindowUndoRedo.cs create mode 100644 Editor/Windows/DialogueWindowUndoRedo.cs.meta create mode 100644 Editor/Windows/FindReplaceWindow.meta create mode 100644 Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs create mode 100644 Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs.meta create mode 100644 Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs create mode 100644 Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs.meta create mode 100644 Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs create mode 100644 Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs.meta create mode 100644 Editor/Windows/FluidDialogueSettings.cs create mode 100644 Editor/Windows/FluidDialogueSettings.cs.meta create mode 100644 Editor/Windows/GraphCrud.cs create mode 100644 Editor/Windows/GraphCrud.cs.meta create mode 100644 Editor/Windows/UserInput.meta create mode 100644 Editor/Windows/UserInput/DelayedMenu.cs create mode 100644 Editor/Windows/UserInput/DelayedMenu.cs.meta create mode 100644 Editor/Windows/UserInput/InputController.cs create mode 100644 Editor/Windows/UserInput/InputController.cs.meta create mode 100644 Editor/Windows/UserInput/LeftClickHandler.cs create mode 100644 Editor/Windows/UserInput/LeftClickHandler.cs.meta create mode 100644 Editor/Windows/UserInput/NodeSelection.cs create mode 100644 Editor/Windows/UserInput/NodeSelection.cs.meta create mode 100644 Editor/Windows/UserInput/RightClickHandler.cs create mode 100644 Editor/Windows/UserInput/RightClickHandler.cs.meta create mode 100644 Editor/Windows/UserInput/ScrollManager.cs create mode 100644 Editor/Windows/UserInput/ScrollManager.cs.meta create mode 100644 Editor/com.fluid.dialogue.Editor.asmdef create mode 100644 Editor/com.fluid.dialogue.Editor.asmdef.meta create mode 100644 LICENSE.md create mode 100644 LICENSE.md.meta create mode 100644 README.md create mode 100644 README.md.meta create mode 100644 Runtime.meta create mode 100644 Runtime/Actions.meta create mode 100644 Runtime/Actions/ActionDataBase.cs create mode 100644 Runtime/Actions/ActionDataBase.cs.meta create mode 100644 Runtime/Actions/ActionRuntime.cs create mode 100644 Runtime/Actions/ActionRuntime.cs.meta create mode 100644 Runtime/Actions/ActionStatus.cs create mode 100644 Runtime/Actions/ActionStatus.cs.meta create mode 100644 Runtime/Actions/IAction.cs create mode 100644 Runtime/Actions/IAction.cs.meta create mode 100644 Runtime/Actions/Libraries.meta create mode 100644 Runtime/Actions/Libraries/Databases.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs create mode 100644 Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs.meta create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs create mode 100644 Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs.meta create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs create mode 100644 Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs.meta create mode 100644 Runtime/Actors.meta create mode 100644 Runtime/Actors/ActorDefinition.cs create mode 100644 Runtime/Actors/ActorDefinition.cs.meta create mode 100644 Runtime/Actors/IActor.cs create mode 100644 Runtime/Actors/IActor.cs.meta create mode 100644 Runtime/Attributes.meta create mode 100644 Runtime/Attributes/CreateMenuAttribute.cs create mode 100644 Runtime/Attributes/CreateMenuAttribute.cs.meta create mode 100644 Runtime/Choices.meta create mode 100644 Runtime/Choices/ChoiceData.cs create mode 100644 Runtime/Choices/ChoiceData.cs.meta create mode 100644 Runtime/Choices/ChoiceRuntime.cs create mode 100644 Runtime/Choices/ChoiceRuntime.cs.meta create mode 100644 Runtime/Choices/IChoice.cs create mode 100644 Runtime/Choices/IChoice.cs.meta create mode 100644 Runtime/Conditions.meta create mode 100644 Runtime/Conditions/ConditionDataBase.cs create mode 100644 Runtime/Conditions/ConditionDataBase.cs.meta create mode 100644 Runtime/Conditions/ConditionRuntime.cs create mode 100644 Runtime/Conditions/ConditionRuntime.cs.meta create mode 100644 Runtime/Conditions/ICondition.cs create mode 100644 Runtime/Conditions/ICondition.cs.meta create mode 100644 Runtime/DialogueController.meta create mode 100644 Runtime/DialogueController/DialogueController.cs create mode 100644 Runtime/DialogueController/DialogueController.cs.meta create mode 100644 Runtime/DialoguePlayback.cs create mode 100644 Runtime/DialoguePlayback.cs.meta create mode 100644 Runtime/Graphs.meta create mode 100644 Runtime/Graphs/DialogueGraph.cs create mode 100644 Runtime/Graphs/DialogueGraph.cs.meta create mode 100644 Runtime/Graphs/Events.meta create mode 100644 Runtime/Graphs/Events/DialogueEvents.cs create mode 100644 Runtime/Graphs/Events/DialogueEvents.cs.meta create mode 100644 Runtime/Graphs/Events/IDialogueEvents.cs create mode 100644 Runtime/Graphs/Events/IDialogueEvents.cs.meta create mode 100644 Runtime/Graphs/GraphRuntime.cs create mode 100644 Runtime/Graphs/GraphRuntime.cs.meta create mode 100644 Runtime/Graphs/IGraph.cs create mode 100644 Runtime/Graphs/IGraph.cs.meta create mode 100644 Runtime/IGetRuntime.cs create mode 100644 Runtime/IGetRuntime.cs.meta create mode 100644 Runtime/ISetup.cs create mode 100644 Runtime/ISetup.cs.meta create mode 100644 Runtime/IUniqueId.cs create mode 100644 Runtime/IUniqueId.cs.meta create mode 100644 Runtime/Nodes.meta create mode 100644 Runtime/Nodes/ChoiceHub.meta create mode 100644 Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs create mode 100644 Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs.meta create mode 100644 Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs create mode 100644 Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs.meta create mode 100644 Runtime/Nodes/Dialogue.meta create mode 100644 Runtime/Nodes/Dialogue/NodeDialogue.cs create mode 100644 Runtime/Nodes/Dialogue/NodeDialogue.cs.meta create mode 100644 Runtime/Nodes/Dialogue/NodeDialogueData.cs create mode 100644 Runtime/Nodes/Dialogue/NodeDialogueData.cs.meta create mode 100644 Runtime/Nodes/Hub.meta create mode 100644 Runtime/Nodes/Hub/NodeHub.cs create mode 100644 Runtime/Nodes/Hub/NodeHub.cs.meta create mode 100644 Runtime/Nodes/Hub/NodeHubData.cs create mode 100644 Runtime/Nodes/Hub/NodeHubData.cs.meta create mode 100644 Runtime/Nodes/INode.cs create mode 100644 Runtime/Nodes/INode.cs.meta create mode 100644 Runtime/Nodes/Links.meta create mode 100644 Runtime/Nodes/Links/NodeLink.cs create mode 100644 Runtime/Nodes/Links/NodeLink.cs.meta create mode 100644 Runtime/Nodes/Links/NodeLinkData.cs create mode 100644 Runtime/Nodes/Links/NodeLinkData.cs.meta create mode 100644 Runtime/Nodes/NodeBase.cs create mode 100644 Runtime/Nodes/NodeBase.cs.meta create mode 100644 Runtime/Nodes/NodeDataBase.cs create mode 100644 Runtime/Nodes/NodeDataBase.cs.meta create mode 100644 Runtime/Nodes/NodeDataChoiceBase.cs create mode 100644 Runtime/Nodes/NodeDataChoiceBase.cs.meta create mode 100644 Runtime/Nodes/NodeNestedDataBase.cs create mode 100644 Runtime/Nodes/NodeNestedDataBase.cs.meta create mode 100644 Runtime/Nodes/Note.meta create mode 100644 Runtime/Nodes/Note/NodeNoteData.cs create mode 100644 Runtime/Nodes/Note/NodeNoteData.cs.meta create mode 100644 Runtime/Nodes/PlayGraph.meta create mode 100644 Runtime/Nodes/PlayGraph/NodePlayGraph.cs create mode 100644 Runtime/Nodes/PlayGraph/NodePlayGraph.cs.meta create mode 100644 Runtime/Nodes/PlayGraph/NodePlayGraphData.cs create mode 100644 Runtime/Nodes/PlayGraph/NodePlayGraphData.cs.meta create mode 100644 Runtime/Nodes/Root.meta create mode 100644 Runtime/Nodes/Root/NodeRoot.cs create mode 100644 Runtime/Nodes/Root/NodeRoot.cs.meta create mode 100644 Runtime/Nodes/Root/NodeRootData.cs create mode 100644 Runtime/Nodes/Root/NodeRootData.cs.meta create mode 100644 Runtime/com.fluid.dialogue.asmdef create mode 100644 Runtime/com.fluid.dialogue.asmdef.meta create mode 100644 Tests.meta create mode 100644 Tests/Editor.meta create mode 100644 Tests/Editor/Actions.meta create mode 100644 Tests/Editor/Actions/ActionRuntimeTest.cs create mode 100644 Tests/Editor/Actions/ActionRuntimeTest.cs.meta create mode 100644 Tests/Editor/Actions/Databases.meta create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs.meta create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs.meta create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs.meta create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs create mode 100644 Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs.meta create mode 100644 Tests/Editor/Actions/Databases/SetLocalBoolTest.cs create mode 100644 Tests/Editor/Actions/Databases/SetLocalBoolTest.cs.meta create mode 100644 Tests/Editor/Builders.meta create mode 100644 Tests/Editor/Builders/A.cs create mode 100644 Tests/Editor/Builders/A.cs.meta create mode 100644 Tests/Editor/Builders/ActionStubBuilder.cs create mode 100644 Tests/Editor/Builders/ActionStubBuilder.cs.meta create mode 100644 Tests/Editor/Builders/ChoiceStubBuilder.cs create mode 100644 Tests/Editor/Builders/ChoiceStubBuilder.cs.meta create mode 100644 Tests/Editor/Builders/DialogueGraphStubBuilder.cs create mode 100644 Tests/Editor/Builders/DialogueGraphStubBuilder.cs.meta create mode 100644 Tests/Editor/Builders/DialogueNodeStubBuilder.cs create mode 100644 Tests/Editor/Builders/DialogueNodeStubBuilder.cs.meta create mode 100644 Tests/Editor/Builders/NodeDataStubBuilder.cs create mode 100644 Tests/Editor/Builders/NodeDataStubBuilder.cs.meta create mode 100644 Tests/Editor/ChoiceRuntimeTest.cs create mode 100644 Tests/Editor/ChoiceRuntimeTest.cs.meta create mode 100644 Tests/Editor/ConditionRuntimeTest.cs create mode 100644 Tests/Editor/ConditionRuntimeTest.cs.meta create mode 100644 Tests/Editor/DialogueControllerTest.cs create mode 100644 Tests/Editor/DialogueControllerTest.cs.meta create mode 100644 Tests/Editor/DialoguePlaybackTest.cs create mode 100644 Tests/Editor/DialoguePlaybackTest.cs.meta create mode 100644 Tests/Editor/GraphRuntimeTest.cs create mode 100644 Tests/Editor/GraphRuntimeTest.cs.meta create mode 100644 Tests/Editor/Nodes.meta create mode 100644 Tests/Editor/Nodes/NodeChoiceHubTest.cs create mode 100644 Tests/Editor/Nodes/NodeChoiceHubTest.cs.meta create mode 100644 Tests/Editor/Nodes/NodeDialogueTest.cs create mode 100644 Tests/Editor/Nodes/NodeDialogueTest.cs.meta create mode 100644 Tests/Editor/Nodes/NodeHubTest.cs create mode 100644 Tests/Editor/Nodes/NodeHubTest.cs.meta create mode 100644 Tests/Editor/Nodes/NodeLinkTest.cs create mode 100644 Tests/Editor/Nodes/NodeLinkTest.cs.meta create mode 100644 Tests/Editor/Nodes/NodePlayGraphTest.cs create mode 100644 Tests/Editor/Nodes/NodePlayGraphTest.cs.meta create mode 100644 Tests/Editor/Nodes/NodeRootTest.cs create mode 100644 Tests/Editor/Nodes/NodeRootTest.cs.meta create mode 100644 Tests/Editor/Utilities.meta create mode 100644 Tests/Editor/Utilities/RectCleanerTest.cs create mode 100644 Tests/Editor/Utilities/RectCleanerTest.cs.meta create mode 100644 Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef create mode 100644 Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef.meta create mode 100644 package.json create mode 100644 package.json.meta diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7a84ea5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +Replaced when project is built from commit logs via Semantic Release. diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 0000000..c0eea1d --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2be8f96e3c5f3ce468af0cb713b8670f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..8ef9d52 --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a5c293c0bd37dd747860a5065cf16604 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Attributes.meta b/Editor/Attributes.meta new file mode 100644 index 0000000..06d4eac --- /dev/null +++ b/Editor/Attributes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 74ec989aa1684dcc941fa8506688f9af +timeCreated: 1563140889 \ No newline at end of file diff --git a/Editor/Attributes/NodeTypeAttribute.cs b/Editor/Attributes/NodeTypeAttribute.cs new file mode 100644 index 0000000..666798a --- /dev/null +++ b/Editor/Attributes/NodeTypeAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class NodeTypeAttribute : Attribute { + public Type Type { get; } + + public NodeTypeAttribute (Type type) { + Type = type; + } + } +} diff --git a/Editor/Attributes/NodeTypeAttribute.cs.meta b/Editor/Attributes/NodeTypeAttribute.cs.meta new file mode 100644 index 0000000..d31128a --- /dev/null +++ b/Editor/Attributes/NodeTypeAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a2ec9dd0c81340f591d47b55ab8bb33a +timeCreated: 1563140911 \ No newline at end of file diff --git a/Editor/CreateDialogueGraph.cs b/Editor/CreateDialogueGraph.cs new file mode 100644 index 0000000..8889435 --- /dev/null +++ b/Editor/CreateDialogueGraph.cs @@ -0,0 +1,59 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public static class CreateDialogueGraph { + [MenuItem("Assets/Create/Fluid/Dialogue/Graph", priority = 0)] + public static void CreateAsset () { + var path = AssetDatabase.GetAssetPath(Selection.activeObject); + CreateAsset(path, "Dialogue"); + + AssetDatabase.SaveAssets(); + } + + /// + /// Create a dialogue graph asset in a specific folder. + /// Designed to create dialogue graphs through custom Unity editor scripts. + /// You must call AssetDatabase.SaveAssets(); on your own to save the asset properly. + /// + public static DialogueGraph CreateAsset (string folderPath, string graphName) { + var graph = CreateGraph(folderPath, graphName); + + var root = ScriptableObject.CreateInstance(); + root.rect.position = + new Vector2(50 + ScrollManager.WINDOW_SIZE /2, 200 + ScrollManager.WINDOW_SIZE / 2); + graph.AddNode(root); + graph.root = root; + + AssetDatabase.AddObjectToAsset(root, graph); + + return graph; + } + + private static DialogueGraph CreateGraph (string folderPath, string graphName) { + var graph = ScriptableObject.CreateInstance(); + graph.name = graphName; + var assetsInPath = AssetDatabase + .FindAssets("t:DialogueGraph", new[] {folderPath}) + .Select(i => { + var p = AssetDatabase.GUIDToAssetPath(i); + var parts = p.Split('/'); + return parts[parts.Length - 1].Replace(".asset", ""); + }) + .ToList(); + + var count = 0; + while (assetsInPath.Find(i => i == graph.name) != null) { + count++; + var name = graph.name.Split('(')[0]; + graph.name = $"{name}({count})"; + } + + AssetDatabase.CreateAsset(graph, $"{folderPath}/{graph.name}.asset"); + return graph; + } + } +} diff --git a/Editor/CreateDialogueGraph.cs.meta b/Editor/CreateDialogueGraph.cs.meta new file mode 100644 index 0000000..2d94c23 --- /dev/null +++ b/Editor/CreateDialogueGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96604ad06ddfa3b4bb0b2895017959b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Inspectors.meta b/Editor/Inspectors.meta new file mode 100644 index 0000000..8d80a1f --- /dev/null +++ b/Editor/Inspectors.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8762dbb8595a43d3a76d7b85ca85d743 +timeCreated: 1563139571 \ No newline at end of file diff --git a/Editor/Inspectors/DialogueGraphInspector.cs b/Editor/Inspectors/DialogueGraphInspector.cs new file mode 100644 index 0000000..a80ce74 --- /dev/null +++ b/Editor/Inspectors/DialogueGraphInspector.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using CleverCrow.Fluid.SimpleSpellcheck; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + [CustomEditor(typeof(DialogueGraph))] + public class DialogueGraphInspector : Editor { + private SerializedProperty _nodes; + + private void OnEnable () { + _nodes = serializedObject.FindProperty("_nodes"); + } + + public override void OnInspectorGUI () { + DrawDefaultInspector(); + + if (GUILayout.Button("Spell Check")) { + RunSpellCheck(); + } + + if (GUILayout.Button("Edit Dialogue")) { + DialogueWindow.ShowGraph(target as DialogueGraph); + } + } + + private void RunSpellCheck () { + var logList = new List(); + + for (var i = 0; i < _nodes.arraySize; i++) { + var node = new SerializedObject(_nodes.GetArrayElementAtIndex(i).objectReferenceValue); + CreateLog(node, logList); + } + + SpellCheck.Instance.ShowLogs($"Dialogue: {target.name}", logList); + } + + private void CreateLog (SerializedObject node, List logList) { + var textProp = node.FindProperty("dialogue"); + var choiceProp = node.FindProperty("choices"); + + if (textProp == null && choiceProp == null) return; + + var textIsInvalid = textProp != null && SpellCheck.Instance.IsInvalid(textProp.stringValue); + + var choiceIsInvalid = false; + if (choiceProp != null) { + for (var j = 0; j < choiceProp.arraySize; j++) { + var choice = choiceProp.GetArrayElementAtIndex(j).objectReferenceValue as ChoiceData; + choiceIsInvalid = SpellCheck.Instance.IsInvalid(choice.text); + if (choiceIsInvalid) break; + } + } + + if (textIsInvalid || choiceIsInvalid) { + var preview = "Invalid choice(s)"; + if (textProp != null) preview = textProp.stringValue; + + var log = new LogEntry(preview, () => { + NodeDataBaseEditor.ShowValidation(node.targetObject as NodeDataBase); + Selection.activeObject = node.targetObject; + }); + + logList.Add(log); + } + } + } +} diff --git a/Editor/Inspectors/DialogueGraphInspector.cs.meta b/Editor/Inspectors/DialogueGraphInspector.cs.meta new file mode 100644 index 0000000..8f911f2 --- /dev/null +++ b/Editor/Inspectors/DialogueGraphInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 85289aea42df4ca7abab7037af86352e +timeCreated: 1563139583 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase.meta b/Editor/Inspectors/NodeDataBase.meta new file mode 100644 index 0000000..f168f24 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3045c912a16b4adab33c18daae5a55cf +timeCreated: 1564456282 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs b/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs new file mode 100644 index 0000000..8216287 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs @@ -0,0 +1,71 @@ +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using CleverCrow.Fluid.SimpleSpellcheck; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + [CustomEditor(typeof(NodeDataBase), true)] + public class NodeDataBaseEditor : Editor { + private SerializedProperty _dialogue; + private SerializedProperty _choices; + + private ConditionSortableList _conditions; + private ActionsSortableList _enterActions; + private ActionsSortableList _exitActions; + + private void OnEnable () { + var node = target as NodeDataBase; + + _dialogue = serializedObject.FindProperty("dialogue"); + _choices = serializedObject.FindProperty("choices"); + + if (!node.HideInspectorConditions) + _conditions = new ConditionSortableList(this, "conditions", node, node.conditions); + + if (!node.HideInspectorActions) { + _enterActions = new ActionsSortableList(this, "enterActions", node, node.enterActions); + _exitActions = new ActionsSortableList(this, "exitActions", node, node.exitActions); + } + } + + public override void OnInspectorGUI () { + base.OnInspectorGUI(); + SpellCheckText(); + + serializedObject.Update(); + + _conditions?.Update(); + _enterActions?.Update(); + _exitActions?.Update(); + + serializedObject.ApplyModifiedProperties(); + } + + private void SpellCheckText () { + if (_dialogue == null && _choices == null) return; + if (!GUILayout.Button("Spell Check")) return; + + ShowValidation(target as NodeDataBase); + } + + public static void ShowValidation (NodeDataBase target) { + var serializedObject = new SerializedObject(target); + var dialogue = serializedObject.FindProperty("dialogue"); + var choices = serializedObject.FindProperty("choices"); + + SpellCheck.Instance.ClearValidation(); + + if (dialogue != null) { + SpellCheck.Instance.AddValidation(dialogue.displayName, dialogue.stringValue); + } + + if (choices != null) { + for (var i = 0; i < choices.arraySize; i++) { + var choice = choices.GetArrayElementAtIndex(i).objectReferenceValue as ChoiceData; + SpellCheck.Instance.AddValidation($"Choice {i}", choice.text); + } + } + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs.meta b/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs.meta new file mode 100644 index 0000000..0587e61 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/NodeDataBaseEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1121105b04294b839ec9ae7f562d00fe +timeCreated: 1564456302 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists.meta b/Editor/Inspectors/NodeDataBase/SortableLists.meta new file mode 100644 index 0000000..026a072 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 112cba4c829b4c169054e9f979f979b6 +timeCreated: 1564888045 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs b/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs new file mode 100644 index 0000000..724a1ff --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class ActionsSortableList : SortableListBase { + private static TypesToMenu _actionTypes; + + private readonly ScriptableObjectListPrinter _soPrinter; + private readonly NestedDataCrud _actionCrud; + + private static TypesToMenu ActionTypes => + _actionTypes ??= new TypesToMenu(); + + public ActionsSortableList (Editor editor, string property, NodeDataBase node, List actions) + : base(editor, property) { + _soPrinter = new ScriptableObjectListPrinter(editor.serializedObject.FindProperty(property)); + _actionCrud = new NestedDataCrud(node, actions, ActionTypes); + + _list.drawElementCallback = _soPrinter.DrawScriptableObject; + _list.elementHeightCallback = _soPrinter.GetHeight; + + _list.onAddDropdownCallback = _actionCrud.ShowMenu; + _list.onRemoveCallback = _actionCrud.DeleteItem; + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs.meta b/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs.meta new file mode 100644 index 0000000..3ac0680 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ActionsSortableList.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c2c72e02a61402d94ebc17e0a9a6dd8 +timeCreated: 1564533764 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs b/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs new file mode 100644 index 0000000..9142a89 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class ConditionSortableList : SortableListBase { + private static TypesToMenu _conditionTypes; + + private ScriptableObjectListPrinter _soPrinter; + private readonly NestedDataCrud _conditionCrud; + + private static TypesToMenu ConditionTypes => + _conditionTypes ??= new TypesToMenu(); + + public ConditionSortableList (Editor editor, string property, NodeDataBase node, List conditions) + : base(editor, property) { + _soPrinter = new ScriptableObjectListPrinter(editor.serializedObject.FindProperty(property)); + _conditionCrud = new NestedDataCrud(node, conditions, ConditionTypes); + + _list.drawElementCallback = _soPrinter.DrawScriptableObject; + _list.elementHeightCallback = _soPrinter.GetHeight; + + _list.onAddDropdownCallback = _conditionCrud.ShowMenu; + _list.onRemoveCallback = _conditionCrud.DeleteItem; + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs.meta b/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs.meta new file mode 100644 index 0000000..6368d17 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ConditionSortableList.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a71b7d60f7bc467ba86208acd17151d9 +timeCreated: 1564949793 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs b/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs new file mode 100644 index 0000000..6f6d3ee --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class NestedDataCrud where T : Object, ISetup { + private readonly NodeDataBase _node; + private readonly List _list; + private readonly TypesToMenu _menuData; + + public NestedDataCrud (NodeDataBase node, List list, TypesToMenu menuData) { + _menuData = menuData; + _node = node; + _list = list; + } + + public void ShowMenu (Rect buttonRect, ReorderableList list) { + var menu = new GenericMenu(); + + foreach (var line in _menuData.Lines) { + menu.AddItem( + new GUIContent(line.path), + false, + () => CreateItem(line.type)); + } + + menu.ShowAsContext(); + } + + private void CreateItem (Type type) { + var graphPath = AssetDatabase.GetAssetPath(_node); + var graph = AssetDatabase.LoadAssetAtPath(graphPath); + + var listItem = ScriptableObject.CreateInstance(type) as T; + Debug.Assert(listItem != null, $"Failed to create new type {type}"); + listItem.Setup(); + + if (FluidDialogueSettings.Current.HideNestedNodeData) { + listItem.hideFlags = HideFlags.HideInHierarchy; + } + + Undo.SetCurrentGroupName("Add type"); + + Undo.RecordObject(graph, "Add type"); + Undo.RecordObject(_node, "Add type"); + + _list.Add(listItem); + AssetDatabase.AddObjectToAsset(listItem, graph); + Undo.RegisterCreatedObjectUndo(listItem, "Add type"); + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + public void DeleteItem (ReorderableList list) { + var graphPath = AssetDatabase.GetAssetPath(_node); + var graph = AssetDatabase.LoadAssetAtPath(graphPath); + var listItem = _list[list.index]; + + Undo.SetCurrentGroupName("Delete type"); + + Undo.RecordObject(graph, "Delete type"); + Undo.RecordObject(_node, "Delete type"); + + _list.Remove(listItem); + Undo.DestroyObjectImmediate(listItem); + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + + // Forcibly refresh the serialized object to prevent an immediate crash + list.serializedProperty.serializedObject.Update(); + + // We must refresh instead of save. Otherwise the re-orderable list will crash due to an editor bug + AssetDatabase.Refresh(); + + // Mark the asset dirty so the changes are saved + EditorUtility.SetDirty(graph); + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs.meta b/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs.meta new file mode 100644 index 0000000..b7ee0dc --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/NestedDataCrud.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e3efb27d8ad46ee84af7f48467af07d +timeCreated: 1564888129 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs b/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs new file mode 100644 index 0000000..5961edc --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class ScriptableObjectListPrinter { + private readonly SerializedProperty _serializedProp; + + private readonly HashSet _variableBlacklist = new HashSet { + "m_Script", + "_uniqueId", + }; + + public ScriptableObjectListPrinter (SerializedProperty serializedProp) { + _serializedProp = serializedProp; + } + + public void DrawScriptableObject (Rect rect, int index, bool active, bool focused) { + var totalHeight = 0f; + + var element = _serializedProp.GetArrayElementAtIndex(index); + if (element.objectReferenceValue == null) { + Debug.LogWarning($"Null element detected in sortable list {element.name}"); + return; + } + + var serializedObject = new SerializedObject(element.objectReferenceValue); + var propIterator = serializedObject.GetIterator(); + + EditorGUI.BeginChangeCheck(); + while (propIterator.NextVisible(true)) { + if (_variableBlacklist.Contains(propIterator.name)) continue; + + var position = new Rect(rect); + position.y += totalHeight; + EditorGUI.PropertyField(position, propIterator, true); + + var height = EditorGUI.GetPropertyHeight(propIterator); + totalHeight += height; + } + if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); + } + + public float GetHeight (int index) { + var totalHeight = EditorGUIUtility.singleLineHeight; + + var element = _serializedProp.GetArrayElementAtIndex(index); + if (element.objectReferenceValue == null) { + Debug.LogWarning($"Null element detected in sortable list {element.name}"); + return 0; + } + + var propIterator = new SerializedObject(element.objectReferenceValue).GetIterator(); + + while (propIterator.NextVisible(true)) { + if (_variableBlacklist.Contains(propIterator.name)) continue; + + var height = EditorGUI.GetPropertyHeight(propIterator); + totalHeight += height; + } + + return totalHeight; + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs.meta b/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs.meta new file mode 100644 index 0000000..16e94dc --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/ScriptableObjectListPrinter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f0a891cdfbfa446b9facbe074d13ff24 +timeCreated: 1564876585 \ No newline at end of file diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs b/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs new file mode 100644 index 0000000..3651fa0 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class TypesToMenu { + public class TypeEntry { + public Type type; + public string path; + public int priority; + } + + public List Lines { get; } + + public TypesToMenu () { + Lines = GetTypeEntries(); + } + + private static List GetTypeEntries () { + var list = new List(); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { + foreach (var type in assembly.GetTypes()) { + if (!type.IsSubclassOf(typeof(T)) || type.IsAbstract) continue; + var attr = type.GetCustomAttribute(); + + list.Add(new TypeEntry { + type = type, + path = attr?.Path ?? type.FullName, + priority = attr?.Priority ?? 0, + }); + } + } + + return list + .OrderByDescending(t => t.priority) + .ToList(); + } + } +} diff --git a/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs.meta b/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs.meta new file mode 100644 index 0000000..4b5e9b3 --- /dev/null +++ b/Editor/Inspectors/NodeDataBase/SortableLists/TypesToMenu.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99afed6d549246e79207f811fd7c8b18 +timeCreated: 1564949929 \ No newline at end of file diff --git a/Editor/Inspectors/SortableListBase.cs b/Editor/Inspectors/SortableListBase.cs new file mode 100644 index 0000000..f59696a --- /dev/null +++ b/Editor/Inspectors/SortableListBase.cs @@ -0,0 +1,37 @@ +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.Inspectors { + public class SortableListBase { + protected ReorderableList _list; + bool _skipFrame; + + public SortableListBase (Editor editor, string property) { + if (editor == null) { + Debug.LogError("Editor cannot be null"); + return; + } + + var prop = editor.serializedObject.FindProperty(property); + if (prop == null) { + Debug.LogErrorFormat("Could not find property {0}", property); + return; + } + + _list = new ReorderableList( + editor.serializedObject, + prop, + true, true, true, true); + + var title = prop.displayName; + _list.drawHeaderCallback = rect => { + EditorGUI.LabelField(rect, title); + }; + } + + public void Update () { + if (_list != null) _list.DoLayoutList(); + } + } +} diff --git a/Editor/Inspectors/SortableListBase.cs.meta b/Editor/Inspectors/SortableListBase.cs.meta new file mode 100644 index 0000000..4669d50 --- /dev/null +++ b/Editor/Inspectors/SortableListBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 23320fdf699a4338a6e0855458753dbb +timeCreated: 1564455524 \ No newline at end of file diff --git a/Editor/NodeEditors.meta b/Editor/NodeEditors.meta new file mode 100644 index 0000000..52d79f0 --- /dev/null +++ b/Editor/NodeEditors.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d6ac8d70d26b4f02916887d315e5867b +timeCreated: 1563140724 \ No newline at end of file diff --git a/Editor/NodeEditors/Base.meta b/Editor/NodeEditors/Base.meta new file mode 100644 index 0000000..a7d7270 --- /dev/null +++ b/Editor/NodeEditors/Base.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b14c477a269346e386c010bde42e4948 +timeCreated: 1564081784 \ No newline at end of file diff --git a/Editor/NodeEditors/Base/HeaderTextStyle.cs b/Editor/NodeEditors/Base/HeaderTextStyle.cs new file mode 100644 index 0000000..9414abf --- /dev/null +++ b/Editor/NodeEditors/Base/HeaderTextStyle.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class HeaderTextStyle { + private GUIStyle _style; + private bool _init; + + public GUIStyle Style { + get { + if (!_init && EditorStyles.centeredGreyMiniLabel != null) { + _init = true; + _style = EditorStyles.centeredGreyMiniLabel; + _style.normal.textColor = ThemeUtility.IsDarkTheme ? Color.white : Color.black; + } else if (_style == null) { + _style = new GUIStyle(); + } + + return _style; + } + } + } +} diff --git a/Editor/NodeEditors/Base/HeaderTextStyle.cs.meta b/Editor/NodeEditors/Base/HeaderTextStyle.cs.meta new file mode 100644 index 0000000..a42a68e --- /dev/null +++ b/Editor/NodeEditors/Base/HeaderTextStyle.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b9615de9e263481c8101a67deb4cce6e +timeCreated: 1563396429 \ No newline at end of file diff --git a/Editor/NodeEditors/Base/NodeEditorBase.cs b/Editor/NodeEditors/Base/NodeEditorBase.cs new file mode 100644 index 0000000..54841e9 --- /dev/null +++ b/Editor/NodeEditors/Base/NodeEditorBase.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public abstract partial class NodeEditorBase { + private const float PADDING_HEADER = 5; + private const float PADDING_CONTENT = 20; + private const float HEADER_HEIGHT = 20; + + private float _cachedContentHeight; + private NodeStyles _styles; + + protected DialogueWindow Window { get; private set; } + private string NodeTitle => string.IsNullOrEmpty(Data.nodeTitle) ? Data.name : Data.nodeTitle; + + protected SerializedObject serializedObject { get; private set; } + public NodeDataBase Data { get; private set; } + private bool IsSelected { get; set; } + + protected virtual Color NodeColor { get; } = new Color(0.7f, 0.7f, 0.7f); + protected virtual float NodeWidth { get; } = 100; + + public bool IsMemoryLeak => Data == null; + public virtual bool Protected => false; + public Rect ContentArea { get; private set; } + + public void Setup (DialogueWindow window, NodeDataBase data) { + Window = window; + Data = data; + _styles = new NodeStyles(NodeColor); + + Data.rect.width = NodeWidth; + serializedObject = new SerializedObject(data); + + CreateConnection(ConnectionType.Out, data, true); + Out[0].Hide = !HasOutConnection; + + CreateConnection(ConnectionType.In, data, true); + In[0].Hide = !HasInConnection; + + OnSetup(); + } + + protected virtual void OnSetup () { + } + + public void Print () { + PositionConnections(); + + if (_cachedContentHeight < 1 || Window.MouseEvents.Scroll.ScrollRect.Overlaps(Data.rect)) { + PrintHeader(); + PrintBody(); + } + + if (!Data.HideConnections) PrintConnections(); + } + + private void PrintHeader () { + var headerBox = new Rect(Data.rect.x, Data.rect.y, Data.rect.width, HEADER_HEIGHT); + var headerArea = new Rect( + headerBox.x + PADDING_HEADER / 2f, + headerBox.y + PADDING_HEADER / 2f, + headerBox.width - PADDING_HEADER, + headerBox.height - PADDING_HEADER); + + var prev = GUI.backgroundColor; + GUI.backgroundColor = _styles.BodyColor; + GUI.Box(headerBox, GUIContent.none, _styles.HeaderStyle.Style); + GUI.backgroundColor = prev; + + GUI.Label(headerArea, NodeTitle, _styles.HeaderTextStyle.Style); + } + + private void PrintBody () { + var box = new Rect(Data.rect.x, Data.rect.y + HEADER_HEIGHT, Data.rect.width, Data.rect.height - HEADER_HEIGHT); + ContentArea = new Rect( + Data.rect.x + PADDING_CONTENT / 2f, + Data.rect.y + HEADER_HEIGHT + PADDING_CONTENT / 2f, + Data.rect.width - PADDING_CONTENT, + Data.rect.height - PADDING_CONTENT); + + var prev = GUI.backgroundColor; + GUI.backgroundColor = _styles.BodyColor; + if (IsSelected) { + GUI.Box(box, GUIContent.none, _styles.ContainerHighlightStyle.Style); + } else { + GUI.Box(box, GUIContent.none, _styles.ContentStyle.Style); + } + GUI.backgroundColor = prev; + + GUILayout.BeginArea(ContentArea); + + GUILayout.BeginVertical(); + OnPrintBody(Event.current); + GUILayout.EndVertical(); + + if (Event.current.type == EventType.Repaint) { + var rect = GUILayoutUtility.GetLastRect(); + if (Math.Abs(rect.height - _cachedContentHeight) > 0.1f) { + Data.rect.height = rect.height + PADDING_CONTENT * 2; + _cachedContentHeight = Data.rect.height; + } + } + + GUILayout.EndArea(); + } + + protected virtual void OnPrintBody (Event e) { + GUILayout.Label(Data.name); + } + + public void Select () { + IsSelected = true; + } + + public void Deselect () { + IsSelected = false; + } + + public void ShowContextMenu () { + if (Protected) return; + + var menu = new GenericMenu(); + menu.AddItem( + new GUIContent("Duplicate"), false, () => { + Window.GraphCrud.DuplicateNode(this); + }); + menu.AddItem( + new GUIContent("Delete"), false, () => { + Window.GraphCrud.DeleteNode(this); + }); + menu.ShowAsContext(); + } + + public NodeDataBase CreateDataCopy () { + var copy = Data.GetDataCopy(); + + var saveNeeded = new List(); + foreach (var action in copy.enterActions.Concat(copy.exitActions)) { + action.Setup(); + saveNeeded.Add(action); + } + + foreach (var condition in copy.conditions) { + condition.Setup(); + saveNeeded.Add(condition); + } + + foreach (var obj in saveNeeded) { + if (FluidDialogueSettings.Current.HideNestedNodeData) { + obj.hideFlags = HideFlags.HideInHierarchy; + } + + AssetDatabase.AddObjectToAsset(obj, Window.Graph); + AssetDatabase.SaveAssets(); + Undo.RegisterCreatedObjectUndo(obj, "Duplicate object"); + } + + return OnCreateDataCopy(copy); + } + + protected virtual NodeDataBase OnCreateDataCopy (NodeDataBase copy) { + return copy; + } + + public void DeleteCleanup () { + OnDeleteCleanup(); + } + + protected virtual void OnDeleteCleanup () { + } + + public bool IsHeaderPosition (Vector2 position) { + var header = new Rect(Data.rect) {height = HEADER_HEIGHT}; + return header.Contains(position); + } + } +} diff --git a/Editor/NodeEditors/Base/NodeEditorBase.cs.meta b/Editor/NodeEditors/Base/NodeEditorBase.cs.meta new file mode 100644 index 0000000..6af7932 --- /dev/null +++ b/Editor/NodeEditors/Base/NodeEditorBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6ef41c03dce84871ad66a4ed229a9272 +timeCreated: 1563148759 \ No newline at end of file diff --git a/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs b/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs new file mode 100644 index 0000000..a12c586 --- /dev/null +++ b/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public abstract partial class NodeEditorBase { + private readonly List _connections = new List(); + private readonly List _out = new List(); + private readonly List _in = new List(); + + protected virtual bool HasOutConnection => true; + protected virtual bool HasInConnection => true; + + public IReadOnlyList Out => _out; + public IReadOnlyList In => _in; + + public Connection GetConnection (Vector2 mousePosition) { + return _connections.Find(c => c.IsClicked(mousePosition)); + } + + public void CreateConnection (ConnectionType type, IConnectionChildCollection childCollection, bool isFirst) { + var connection = new Connection(type, Data, childCollection, Window, isFirst); + + _connections.Add(connection); + switch (type) { + case ConnectionType.In: + _in.Add(connection); + break; + case ConnectionType.Out: + _out.Add(connection); + break; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + + public void RemoveConnection (Connection connection) { + _connections.Remove(connection); + switch (connection.Type) { + case ConnectionType.In: + _in.Remove(connection); + break; + case ConnectionType.Out: + _out.Remove(connection); + break; + default: + throw new ArgumentOutOfRangeException(nameof(connection.Type), connection.Type, null); + } + } + + private void PositionConnections () { + if (Out.Count > 0) { + var outPosition = Data.rect.position; + outPosition.x += Data.rect.width - Connection.SIZE / 2; + outPosition.y += Data.rect.height / 2 - Connection.SIZE / 2; + Out[0].SetPosition(outPosition); + } + + if (In.Count > 0) { + var inPosition = Data.rect.position; + inPosition.x -= Connection.SIZE / 2; + inPosition.y += Data.rect.height / 2 - Connection.SIZE / 2; + In[0].SetPosition(inPosition); + } + } + + public void CleanConnections () { + foreach (var connection in In) { + foreach (var parent in connection.Parents) { + parent.UndoRecordAllObjects(); + parent.Links.RemoveLink(connection); + } + } + } + + private void PrintConnections () { + foreach (var connection in _connections) { + connection.Print(); + } + } + } +} diff --git a/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs.meta b/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs.meta new file mode 100644 index 0000000..492264e --- /dev/null +++ b/Editor/NodeEditors/Base/NodeEditorBaseConnections.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7612f7736e0646fc8c77f80704c1a8e4 +timeCreated: 1564082574 \ No newline at end of file diff --git a/Editor/NodeEditors/Base/NodeStyles.cs b/Editor/NodeEditors/Base/NodeStyles.cs new file mode 100644 index 0000000..89cfa9a --- /dev/null +++ b/Editor/NodeEditors/Base/NodeStyles.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public class NodeStyles { + public NodeBoxStyle ContentStyle { get; } + public NodeBoxStyle HeaderStyle { get; } + public NodeBoxStyle ContainerHighlightStyle { get; } + public HeaderTextStyle HeaderTextStyle { get; } + public Color BodyColor { get; } + + public NodeStyles (Color bodyColor) { + BodyColor = bodyColor; + + ContentStyle = new NodeBoxStyle(BodyColor, BodyColor); + var brightBodyColor = BodyColor * 1.3f; + ContainerHighlightStyle = new NodeBoxStyle(Color.black, brightBodyColor); + HeaderTextStyle = new HeaderTextStyle(); + + var headerColor = BodyColor * 1.3f; + headerColor.a = 1f; + HeaderStyle = new NodeBoxStyle(headerColor, headerColor); + } + } +} diff --git a/Editor/NodeEditors/Base/NodeStyles.cs.meta b/Editor/NodeEditors/Base/NodeStyles.cs.meta new file mode 100644 index 0000000..517f289 --- /dev/null +++ b/Editor/NodeEditors/Base/NodeStyles.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fe933ffda0f24412bfa2f3be3ba6d611 +timeCreated: 1564081860 \ No newline at end of file diff --git a/Editor/NodeEditors/ChoiceEditor.cs b/Editor/NodeEditors/ChoiceEditor.cs new file mode 100644 index 0000000..a4d070a --- /dev/null +++ b/Editor/NodeEditors/ChoiceEditor.cs @@ -0,0 +1,42 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodeChoiceHubData))] + public class ChoiceEditor : NodeEditorBase { + private ChoiceCollection _choices; + + protected override Color NodeColor { get; } = new Color(0.33f, 0.75f, 0.73f); + protected override float NodeWidth { get; } = 200; + + protected override void OnSetup () { + _choices = new ChoiceCollection(this, Data as NodeChoiceHubData, Window); + In[0].IsValidLinkTargetCallback = (i) => i.IsFirst && i.Data is NodeDialogueData; + } + + protected override void OnPrintBody (Event e) { + serializedObject.Update(); + + _choices.Print(e); + CreateChoice(); + + serializedObject.ApplyModifiedProperties(); + } + + private void CreateChoice () { + if (GUILayout.Button("Add Choice", EditorStyles.miniButton, GUILayout.Width(80))) { + GUI.FocusControl(null); + _choices.Add(); + } + } + + protected override NodeDataBase OnCreateDataCopy (NodeDataBase copy) { + return _choices.GetParentDataCopy(copy as NodeDataChoiceBase); + } + + protected override void OnDeleteCleanup () { + _choices.DeleteAll(); + } + } +} diff --git a/Editor/NodeEditors/ChoiceEditor.cs.meta b/Editor/NodeEditors/ChoiceEditor.cs.meta new file mode 100644 index 0000000..a688368 --- /dev/null +++ b/Editor/NodeEditors/ChoiceEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 11c1738a686a48ae9d7b08663d621d83 +timeCreated: 1564348565 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections.meta b/Editor/NodeEditors/Connections.meta new file mode 100644 index 0000000..04b5b81 --- /dev/null +++ b/Editor/NodeEditors/Connections.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8a9784d49f3549d5b3f5cec4e979c270 +timeCreated: 1563666054 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections/Connection.cs b/Editor/NodeEditors/Connections/Connection.cs new file mode 100644 index 0000000..2debcb5 --- /dev/null +++ b/Editor/NodeEditors/Connections/Connection.cs @@ -0,0 +1,124 @@ +using System; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public partial interface IConnection { + ConnectionType Type { get; } + Rect Rect { get; } + NodeDataBase Data { get; } + IConnectionLinks Links { get; } + bool IsFirst { get; } + void UndoRecordAllObjects (); + bool IsValidLinkTarget (IConnection target); + } + + public partial class Connection : IConnection { + public const float SIZE = 16; + private static Texture2D _graphic; + + private Rect _rect = new Rect(Vector2.zero, new Vector2(SIZE, SIZE)); + private readonly IConnectionChildCollection _childCollection; + + public Func IsValidLinkTargetCallback { private get; set; } = (i) => true; + public ConnectionType Type { get; } + public Rect Rect => _rect; + public NodeDataBase Data { get; } + public IConnectionLinks Links { get; } + public bool IsFirst { get; } + + public IDialogueWindow Window { get; } + + private static Texture2D Graphic { + get { + if (_graphic == null) _graphic = Resources.Load("dot"); + return _graphic; + } + } + + private bool IsMemoryLeak => _childCollection.Children.Count != Links.List.Count; + public bool Hide { private get; set; } + + public Connection (ConnectionType type, NodeDataBase data, IConnectionChildCollection childCollection, IDialogueWindow window, bool isFirst) { + IsFirst = isFirst; + Window = window; + Type = type; + Data = data; + _childCollection = childCollection; + Links = new ConnectionLinks(this, childCollection); + } + + public void Print () { + if (Hide) return; + + if (IsMemoryLeak) { + Links.RebuildLinks(); + } + + var color = GUI.color; + GUI.color = GetGraphicColor(); + + GUI.DrawTexture(_rect, Graphic); + + foreach (var connection in Links.List) { + PaintCurve(connection.Rect.center); + } + + if (_exampleCurveActive) { + PaintCurve(_exampleCurveTarget); + } + + GUI.color = color; + } + + public bool IsClicked (Vector2 mousePosition) { + return !Hide && _rect.Contains(mousePosition); + } + + public void SetPosition (Vector2 position) { + _rect.position = position; + } + + public void ShowContextMenu () { + if (Type == ConnectionType.In) return; + + var menu = new GenericMenu(); + menu.AddItem( + new GUIContent("Clear Connections"), false, () => { + Undo.RecordObject(_childCollection as Object, "Clear connections"); + Links.ClearAllLinks(); + _childCollection.ClearConnectionChildren(); + DialogueWindow.SaveGraph(); + }); + menu.ShowAsContext(); + } + + public void UndoRecordAllObjects () { + // Sometimes goes null when bulk deleting nodes + if (Data == null) return; + + Undo.RecordObject(Data, "Changed connection"); + if (!(_childCollection is NodeDataBase)) { + Undo.RecordObject(_childCollection as Object, "Changed connection"); + } + } + + private Color GetGraphicColor () { + switch (Type) { + case ConnectionType.In: + var color = Color.gray; + color.a = 1f; + return color; + case ConnectionType.Out: + return Color.cyan; + default: + throw new System.ArgumentOutOfRangeException(); + } + } + public bool IsValidLinkTarget (IConnection target) { + return IsValidLinkTargetCallback.Invoke(target); + } + } +} diff --git a/Editor/NodeEditors/Connections/Connection.cs.meta b/Editor/NodeEditors/Connections/Connection.cs.meta new file mode 100644 index 0000000..9500acb --- /dev/null +++ b/Editor/NodeEditors/Connections/Connection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d6cf4596609141088fab54d90aad4edb +timeCreated: 1563662713 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections/ConnectionCurves.cs b/Editor/NodeEditors/Connections/ConnectionCurves.cs new file mode 100644 index 0000000..03c87f1 --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionCurves.cs @@ -0,0 +1,34 @@ +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public partial class Connection { + private bool _exampleCurveActive; + private Vector2 _exampleCurveTarget; + + public void SetExampleCurve (Vector2 position) { + _exampleCurveActive = true; + _exampleCurveTarget = position; + } + + public void ClearCurveExample () { + _exampleCurveActive = false; + } + + private void PaintCurve (Vector2 destination) { + var curveMaxDistance = Vector2.Distance(_rect.center, destination) / 200; + var curveWeight = Mathf.Lerp(0, 50, curveMaxDistance); + + Handles.DrawBezier( + _rect.center, + destination, + _rect.center + Vector2.right * curveWeight, + destination + Vector2.left * curveWeight, + Color.cyan, + null, + 2f + ); + GUI.changed = true; + } + } +} diff --git a/Editor/NodeEditors/Connections/ConnectionCurves.cs.meta b/Editor/NodeEditors/Connections/ConnectionCurves.cs.meta new file mode 100644 index 0000000..44e3251 --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionCurves.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 840d77ce845f472abe37335f93b339ea +timeCreated: 1564082914 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections/ConnectionLinks.cs b/Editor/NodeEditors/Connections/ConnectionLinks.cs new file mode 100644 index 0000000..802a15d --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionLinks.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using Object = UnityEngine.Object; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public interface IConnectionLinks { + IReadOnlyList List { get; } + + void AddLink (IConnection connection); + void RemoveLink (IConnection connection); + void RebuildLinks (); + void ClearAllLinks (); + } + + public class ConnectionLinks : IConnectionLinks { + private readonly Connection _owner; + private readonly List _list = new List(); + private readonly IConnectionChildCollection _childCollection; + + public IReadOnlyList List => _list; + + public ConnectionLinks (Connection owner, IConnectionChildCollection childCollection) { + _owner = owner; + _childCollection = childCollection; + } + + public void AddLink (IConnection target) { + if (target == null + || target.Type == _owner.Type + || !_owner.IsValidLinkTarget(target) + || !target.IsValidLinkTarget(_owner) + || _list.Contains(target)) return; + + if (_owner.Type == ConnectionType.In) { + target.Links.AddLink(_owner); + return; + } + + BindLink(target); + + if (_childCollection is Object data) { + Undo.RecordObject(data, "Add connection"); + } + + _childCollection.AddConnectionChild(target.Data); + _childCollection.SortConnectionsByPosition(); + } + + public void RemoveLink (IConnection connection) { + _list.Remove(connection); + _childCollection.RemoveConnectionChild(connection.Data); + } + + public void RebuildLinks () { + if (_owner.Type == ConnectionType.In) { + return; + } + + ClearAllLinks(); + foreach (var child in _childCollection.Children) { + var target = _owner.Window.DataToNode[child]; + BindLink(target.In[0]); + } + } + + public void ClearAllLinks () { + _list.ForEach(c => c?.RemoveParent(_owner)); + _list.Clear(); + } + + private void BindLink (IConnection target) { + _list.Add(target); + target.AddParent(_owner); + } + } +} diff --git a/Editor/NodeEditors/Connections/ConnectionLinks.cs.meta b/Editor/NodeEditors/Connections/ConnectionLinks.cs.meta new file mode 100644 index 0000000..d8590c1 --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionLinks.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 82ba925e84324b809672b17c79051bd3 +timeCreated: 1563940811 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections/ConnectionParents.cs b/Editor/NodeEditors/Connections/ConnectionParents.cs new file mode 100644 index 0000000..dbd10be --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionParents.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public partial interface IConnection { + void AddParent (IConnection parent); + void RemoveParent (IConnection parent); + } + + public partial class Connection { + private readonly List _parents = new List(); + + public IReadOnlyList Parents => _parents; + + public void AddParent (IConnection parent) { + _parents.Add(parent); + } + + public void RemoveParent (IConnection parent) { + _parents.Remove(parent); + } + } +} diff --git a/Editor/NodeEditors/Connections/ConnectionParents.cs.meta b/Editor/NodeEditors/Connections/ConnectionParents.cs.meta new file mode 100644 index 0000000..9f6add9 --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionParents.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8df34f07102c4a1582ff48b91c3e59e3 +timeCreated: 1563931560 \ No newline at end of file diff --git a/Editor/NodeEditors/Connections/ConnectionType.cs b/Editor/NodeEditors/Connections/ConnectionType.cs new file mode 100644 index 0000000..d3d876c --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionType.cs @@ -0,0 +1,6 @@ +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public enum ConnectionType { + In, + Out, + } +} diff --git a/Editor/NodeEditors/Connections/ConnectionType.cs.meta b/Editor/NodeEditors/Connections/ConnectionType.cs.meta new file mode 100644 index 0000000..9550690 --- /dev/null +++ b/Editor/NodeEditors/Connections/ConnectionType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ebce98c1aa954cfe9da799aa85c83666 +timeCreated: 1563734482 \ No newline at end of file diff --git a/Editor/NodeEditors/Dialogue.meta b/Editor/NodeEditors/Dialogue.meta new file mode 100644 index 0000000..7a49949 --- /dev/null +++ b/Editor/NodeEditors/Dialogue.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d9fccf96dfb84d3581d12de9aa63345d +timeCreated: 1564336506 \ No newline at end of file diff --git a/Editor/NodeEditors/Dialogue/ChoiceCollection.cs b/Editor/NodeEditors/Dialogue/ChoiceCollection.cs new file mode 100644 index 0000000..cdffceb --- /dev/null +++ b/Editor/NodeEditors/Dialogue/ChoiceCollection.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + public class ChoiceCollection { + protected readonly DialogueWindow _window; + protected readonly NodeEditorBase _node; + protected readonly NodeDataChoiceBase _data; + + protected readonly List _graveyard = new List(); + protected readonly List _connections = new List(); + protected readonly List _serializedObjects = new List(); + protected readonly Stack _callbacks = new Stack(); + protected readonly SerializedObject _serializedData; + protected readonly SerializedProperty _propChoices; + + private bool IsChoiceMemoryLeak => _data.choices.Count != _connections.Count; + + public ChoiceCollection (NodeEditorBase node, NodeDataChoiceBase data, DialogueWindow window) { + _window = window; + _node = node; + _data = data; + _serializedData = new SerializedObject(data); + _propChoices = _serializedData.FindProperty("choices"); + + RebuildChoices(); + } + + public void Add () { + Undo.SetCurrentGroupName("Add choice"); + Undo.RecordObject(_data, "Add choice"); + + var choice = CreateChoice(); + + // Field should be overridable + _data.choices.Add(choice); + + if (FluidDialogueSettings.Current.HideNestedNodeData) { + choice.hideFlags = HideFlags.HideInHierarchy; + } + + AssetDatabase.AddObjectToAsset(choice, _data); + AssetDatabase.SaveAssets(); + + AddConnectionDisplay(choice); + + Undo.RegisterCreatedObjectUndo(choice, "Add choice"); + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + protected virtual ChoiceData CreateChoice () { + var choice = ScriptableObject.CreateInstance(); + choice.name = "Choice"; + choice.Setup(); + + return choice; + } + + public void Print (Event e) { + _serializedData.Update(); + + MemoryLeakCleaner(); + PrintChoices(e); + + while (_callbacks.Count > 0) { + _callbacks.Pop().Invoke(); + } + + CleanGraveyard(); + + _serializedData.ApplyModifiedProperties(); + } + + public void DeleteAll () { + foreach (var choice in _data.choices.ToList()) { + Undo.DestroyObjectImmediate(choice); + } + } + + public NodeDataBase GetParentDataCopy (NodeDataChoiceBase copy) { + foreach (var choice in copy.choices) { + choice.Setup(); + + if (FluidDialogueSettings.Current.HideNestedNodeData) { + choice.hideFlags = HideFlags.HideInHierarchy; + } + + AssetDatabase.AddObjectToAsset(choice, _window.Graph); + AssetDatabase.SaveAssets(); + Undo.RegisterCreatedObjectUndo(choice, "Duplicate choice"); + } + + return copy; + } + + protected virtual void PrintChoices (Event e) { + for (var i = 0; i < _propChoices.arraySize; i++) { + GUILayout.BeginHorizontal(); + + var choice = _propChoices.GetArrayElementAtIndex(i).objectReferenceValue as ChoiceData; + var connection = _connections[i]; + var serializedObject = _serializedObjects[i]; + + EditorGUI.BeginChangeCheck(); + serializedObject.Update(); + if (GUILayout.Button("Edit", EditorStyles.miniButton)) { + ShowEditMenu(choice, i); + } + + EditorGUILayout.PropertyField(serializedObject.FindProperty("text"), GUIContent.none); + if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); + + GUILayout.EndHorizontal(); + + // Only draw on repaint events to prevent crashing display position + if (e.type == EventType.Repaint) { + var area = GUILayoutUtility.GetLastRect(); + var pos = _node.ContentArea.position; + pos.x += _node.ContentArea.width; + pos.y += area.y; + connection.SetPosition(pos); + } + } + } + + protected void ShowEditMenu (ChoiceData choice, int index) { + GUI.FocusControl(null); + var menu = new GenericMenu(); + + menu.AddItem(new GUIContent("View"), false, () => { + Selection.activeObject = choice; + }); + + menu.AddItem(new GUIContent("Move Up"), false, () => { + _callbacks.Push(() => { + MoveChoice(choice, index, -1); + DialogueWindow.SaveGraph(); + }); + }); + + menu.AddItem(new GUIContent("Move Down"), false, () => { + _callbacks.Push(() => { + MoveChoice(choice, index, 1); + DialogueWindow.SaveGraph(); + }); + }); + + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Delete"), false, () => { + _graveyard.Add(_data.choices[index]); + }); + + menu.ShowAsContext(); + } + + private void MoveChoice (ChoiceData choice, int index, int change) { + var newIndex = Mathf.Min(Mathf.Max(index + change, 0), _data.choices.Count - 1); + _data.choices.RemoveAt(index); + _data.choices.Insert(newIndex, choice); + RebuildChoices(); + } + + private void AddConnectionDisplay (ChoiceData choice) { + _node.Out[0].Hide = true; + _node.CreateConnection(ConnectionType.Out, choice, false); + _connections.Add(_node.Out[_node.Out.Count - 1]); + _serializedObjects.Add(new SerializedObject(choice)); + } + + private void RebuildChoices () { + _node.Out[0].Hide = false; + _connections.ForEach(_node.RemoveConnection); + _connections.Clear(); + _serializedObjects.Clear(); + foreach (var choice in _data.choices) { + AddConnectionDisplay(choice); + } + } + + private void MemoryLeakCleaner () { + if (IsChoiceMemoryLeak) { + RebuildChoices(); + } + } + + private void DeleteChoice (ChoiceData choice) { + var choiceIndex = _data.choices.IndexOf(choice); + var connection = _connections[choiceIndex]; + var serializedObject = _serializedObjects[choiceIndex]; + + Undo.SetCurrentGroupName($"Delete {choice.name}"); + Undo.RecordObject(_data, $"Delete {choice.name}"); + + connection.Links.ClearAllLinks(); + _data.choices.Remove(choice); + _node.RemoveConnection(connection); + _connections.Remove(connection); + _serializedObjects.Remove(serializedObject); + + Undo.DestroyObjectImmediate(choice); + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + private void CleanGraveyard () { + if (_graveyard.Count <= 0) return; + + foreach (var choice in _graveyard) { + DeleteChoice(choice); + } + + _node.Out[0].Hide = _connections.Count != 0; + _graveyard.Clear(); + AssetDatabase.SaveAssets(); + } + } +} diff --git a/Editor/NodeEditors/Dialogue/ChoiceCollection.cs.meta b/Editor/NodeEditors/Dialogue/ChoiceCollection.cs.meta new file mode 100644 index 0000000..589085a --- /dev/null +++ b/Editor/NodeEditors/Dialogue/ChoiceCollection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 020d318af0bb4c5780138da65e71c76d +timeCreated: 1564260611 \ No newline at end of file diff --git a/Editor/NodeEditors/Dialogue/DialogueEditor.cs b/Editor/NodeEditors/Dialogue/DialogueEditor.cs new file mode 100644 index 0000000..6f827d9 --- /dev/null +++ b/Editor/NodeEditors/Dialogue/DialogueEditor.cs @@ -0,0 +1,44 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodeDialogueData))] + public class DialogueEditor : NodeEditorBase { + private ChoiceCollection _choices; + + protected override Color NodeColor { get; } = new Color(0.28f, 0.75f, 0.34f); + protected override float NodeWidth { get; } = 200; + + protected override void OnSetup () { + _choices = new ChoiceCollection(this, Data as NodeDataChoiceBase, Window); + } + + protected override void OnPrintBody (Event e) { + serializedObject.Update(); + + EditorGUILayout.PropertyField(serializedObject.FindProperty("actor"), GUIContent.none); + EditorGUILayout.PropertyField(serializedObject.FindProperty("dialogue"), GUIContent.none); + + _choices.Print(e); + CreateChoice(); + + serializedObject.ApplyModifiedProperties(); + } + + private void CreateChoice () { + if (GUILayout.Button("Add Choice", EditorStyles.miniButton, GUILayout.Width(80))) { + GUI.FocusControl(null); + _choices.Add(); + } + } + + protected override NodeDataBase OnCreateDataCopy (NodeDataBase copy) { + return _choices.GetParentDataCopy(copy as NodeDataChoiceBase); + } + + protected override void OnDeleteCleanup () { + _choices.DeleteAll(); + } + } +} diff --git a/Editor/NodeEditors/Dialogue/DialogueEditor.cs.meta b/Editor/NodeEditors/Dialogue/DialogueEditor.cs.meta new file mode 100644 index 0000000..8a848ac --- /dev/null +++ b/Editor/NodeEditors/Dialogue/DialogueEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3ca57f1b0134c7f8d6042385d23bbea +timeCreated: 1563409064 \ No newline at end of file diff --git a/Editor/NodeEditors/HubEditor.cs b/Editor/NodeEditors/HubEditor.cs new file mode 100644 index 0000000..6e17a67 --- /dev/null +++ b/Editor/NodeEditors/HubEditor.cs @@ -0,0 +1,9 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodeHubData))] + public class HubEditor : NodeEditorBase { + protected override Color NodeColor { get; } = new Color(0.7f, 0.7f, 0.7f); + } +} diff --git a/Editor/NodeEditors/HubEditor.cs.meta b/Editor/NodeEditors/HubEditor.cs.meta new file mode 100644 index 0000000..6099921 --- /dev/null +++ b/Editor/NodeEditors/HubEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 30027bb6c563437d9d4f24872199aa31 +timeCreated: 1564346231 \ No newline at end of file diff --git a/Editor/NodeEditors/NoteEditor.cs b/Editor/NodeEditors/NoteEditor.cs new file mode 100644 index 0000000..1e7f4de --- /dev/null +++ b/Editor/NodeEditors/NoteEditor.cs @@ -0,0 +1,18 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodeNoteData))] + public class NoteEditor : NodeEditorBase { + protected override Color NodeColor { get; } = Color.yellow; + protected override float NodeWidth { get; } = 200; + + protected override void OnPrintBody (Event e) { + serializedObject.Update(); + EditorGUILayout.PropertyField(serializedObject.FindProperty("note"), GUIContent.none); + serializedObject.ApplyModifiedProperties(); + } + + } +} diff --git a/Editor/NodeEditors/NoteEditor.cs.meta b/Editor/NodeEditors/NoteEditor.cs.meta new file mode 100644 index 0000000..4714d3f --- /dev/null +++ b/Editor/NodeEditors/NoteEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 791aabda2583449390ea07cfba17cf88 +timeCreated: 1713805041 \ No newline at end of file diff --git a/Editor/NodeEditors/PlayGraphEditor.cs b/Editor/NodeEditors/PlayGraphEditor.cs new file mode 100644 index 0000000..5092ae7 --- /dev/null +++ b/Editor/NodeEditors/PlayGraphEditor.cs @@ -0,0 +1,17 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodePlayGraphData))] + public class PlayGraphEditor : NodeEditorBase { + protected override Color NodeColor { get; } = new Color(0.75f, 0.52f, 0f); + protected override float NodeWidth { get; } = 200; + + protected override void OnPrintBody (Event e) { + serializedObject.Update(); + EditorGUILayout.PropertyField(serializedObject.FindProperty("dialogueGraph"), GUIContent.none); + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/NodeEditors/PlayGraphEditor.cs.meta b/Editor/NodeEditors/PlayGraphEditor.cs.meta new file mode 100644 index 0000000..1729fb2 --- /dev/null +++ b/Editor/NodeEditors/PlayGraphEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e0adb2e523ba4086b6e5ce244b1fc6e5 +timeCreated: 1580321152 \ No newline at end of file diff --git a/Editor/NodeEditors/RootEditor.cs b/Editor/NodeEditors/RootEditor.cs new file mode 100644 index 0000000..f7b3777 --- /dev/null +++ b/Editor/NodeEditors/RootEditor.cs @@ -0,0 +1,9 @@ +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Editors.NodeDisplays { + [NodeType(typeof(NodeRootData))] + public class RootEditor : NodeEditorBase { + public override bool Protected => true; + protected override bool HasInConnection => false; + } +} diff --git a/Editor/NodeEditors/RootEditor.cs.meta b/Editor/NodeEditors/RootEditor.cs.meta new file mode 100644 index 0000000..cff6e27 --- /dev/null +++ b/Editor/NodeEditors/RootEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f26d4da6d67446879a0d778cfdb1cec3 +timeCreated: 1563140738 \ No newline at end of file diff --git a/Editor/Resources.meta b/Editor/Resources.meta new file mode 100644 index 0000000..154aa28 --- /dev/null +++ b/Editor/Resources.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2dfa7f257974486ba4e9324ae1c3b843 +timeCreated: 1563666273 \ No newline at end of file diff --git a/Editor/Resources/dot.png b/Editor/Resources/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..36c0ce937490b4380137cd287a00095f16c59b31 GIT binary patch literal 18297 zcmeI33p7;g`^UGVkXtIcNsUV-nfsVA(-@a=4GANo`%cx z+~P$J8miM(0RYf&aPe1xce4of9GjUFe&2khs`_aw$|_RTNCeHhkfSf@GgJWH zhf}HZ4a$`-016>@)Kq|S*R*oXjF^G(FKzZNSBTi6P%cd{cUFne2CTw%*c<_@>=h!? zQ|5RAnPY&}H`u-{z+xn@dhW*OcYuiW_j}hX0Lymk>nLO<0EkIotS#WT0LZUhvegb? zVgPNfa}^nAKmr&iZ>}S7;w(^JqosBVP*Ddk?y*txfHCWV)mIG-Lx3%N0d4y?-qgS5 z7ihJcK&1Abq81yG?IM?{AcK^>y-_obYn-Oc*Cw)lVWnE3Dns{9!$lIt|K5EQ05TJF zpw)Kwh16&k)YK4n6ltzPzPzY3VBqW9(%)8_C$a*7r_%7cep7Ve^oRvY5rY1Mv%ALl ztyIfg+q;c>YP{7MAhY90gy6(N9pI5o6+`BK}?{Mm; z-nYMBcxDudTM_9n=Kb%LH+x*NX4LK)x5c;VdA!4?vuXpM^zIl`Ir$bdO|)CwCkE}X zP2Jv&%QA?w*){L-?D_%Dvu4weinL}k;KUl5!ukZ{(G_gPcg}OQn9}lRy#Vm0L|Adx zSVb|yZ}ZdI;DOhceGZwkfe5bCt{?zdX={XHTrae|q5=T6nUN+ZtaW=Trka*3&8QgL zRH621HF1lLQB|dlmW?VWVupD2y!=fz#?iM<=$WiW-?h>sl)L*yCyF#tm0ssGEkxr# zuv9WCj7uZOjj_I}GKr=CC|1FDE7Nd?%9OoP@AWGj$4uO2gt!)~?QYCYe%LFb5VoiQN#=*!h}cTNaGZ@xL3|KNIdF%dd~R?e^*WQ z#bINZj;4n9C&o`gYpsu^SCNb+#?f(ei+BFwbn-Bxeqz0SeV~V7hiOh4!Ew4_;rvn6NT-K0FP-1Nn- zJas*JISY=)RBlNLdcRM)Xk5E-c5-+4)+gGp=RKRJ zdxYRpj0Knb=O!g5G4mYr-18h>l@SX%v(t~1*_HTwjMTKgvT*M5xemPenz8Bao)0}A zXRBsUcD10qEkNb^XXzd&u?@Z$nBA3P)3V zbt9!l^_r>F%Vt4Bsvi1E<6rLd6_>2&S*$&*XW9+XY;=P6u?Hv4W(DW=vR+T2y`zcux6oRsq$jv1KC5n$%FkXsd&BLL zFk@9zGkv4s0B0ahJLXyXGqslWQ&P}ZSBOFn)KvCFWlzpQO*Ew>S-He6GT;5DwU)`z$=heYnG`&G zhiYi0UM0@}T}P~oZJ;kVnYrkVqls(Su0Ik=%|eYXBMa<&~BWPth2GfxGAJfH%55<;@+m+r*rbEiaE91dTz(_ zC&-Ltnn}6I&yzI+H6A`Xt$iQ?&kRMj zp{*Ugksq&p!>-2bf=ruN}Z&K~kJX1d|^Idk{?Ix}6{>vv}pBx%z zjo(M0`h+g*W?bVx>ezHAk@u!{d-|UA4dJN2n^rd-ELz{ru4!H~UikXFmh@@8-N3f? z1*HpWmcKbCeRHa@ZIbsn@6_XzRf3n&m(>2mJ8oA>_LZa-6fgHL?g}eAv!2`Ap_@Uf zdE8SL`q$)v`}Z=)7LUui!-Tz;d*3!sejF~I+gEd=vZD#M&cCO@+j_jP>Arq^<6 z=VVOjKiGexV9I24`1^aW9@NyO)E!8Elbm=_@M~7z%ko##jyG0UR?oa}Ed-BU*|X$h zXGL&Ru-@xK&Ewr#e?@FG9`O77bakPy zg#r|dBlHDPAp#NfAPN9f%McNZ?GH*3z95euXkqZWWR8I)O_dr#fdU9XDGL!I2ndu=LM#l1;!>dV!Dh4pVyKJM-@?Fpa6kmp zbuof26oUve6cNeB5J?CU8HF_?kq9JH1P+5WLu2r0JO+uwQwSIe0f+efF`%hJXH+qV zOYyL^|2!OISs3_9r6LL%9UL5t3dW;^VjdbxCX>+^92$p1LOqa@&_F3G1Q{qX91ij& zjx8u*i}@lcUl@oOjLY&Bu9aFC7z_^d_49dM0@2rj0wtf>K@`y;ED;)u!k~X5a&?t0 z69|5<}H?K{NThvL6m8VT6i6v;IO~O5v>&m4DsQx(O>{5 zfFu%V^4K3vLG(gLXA&kBGfgEA5WF8d_myGQ9NHoxvB?W1al7z&NkvJR! zhozViDOkdM432`qj0!T`6Bff2%6$$?%K9z}j)FC(V6da3eA5>mN66uGL;q2fk)Gei zaCN0P1xlo>KsM-POM}*q;`2Ea5+1~12plpJL*ihOY>+@knwztHkvMZA#>~u|jmHx? zLyP=Y{#|HWA$#p$B0-^tFMN)W4Tb-j9uzi~Kw@Lr93+{`<|46Jkc4F6F(f1hkK>qw zM7%kM%>FXas9@g>Gn#$k}=WGsjTxdb*e7>K{;s;^A8_JO{fwC!dRF*-xHdJ{$!Juzjzc>Q;Lls2C5=+3r3)8~j%a!@H z(sTJd#Ktkhma;Oysb+U$*(bd>clxnC-_39xgo&COsQaNl2S11;+q%^*O#RJizKpqu6(m0YW)RDUfT^Jw~ zy9zlV4Npb?sQHH~=sVNJ!(W-wBpwbn2q!x_5lbc#kvJ4~beK{7M#UL?JfT4M38-eV zhUzn<`K}G#pE+3X1J(}p$76;*XzbwOvv#x}uJ!p}w4?oS?TFMXkT0b{_qm~|!kR~^ z2A`rRLs&hb2Y!$SEe_rU@4c8G0tPT``Otk{GI+nj50uvqdzU_fM>a$K_cz`Sx?Fd(u`IWF0J zU|u;c7!cW}9G7f9Fs~dJ42Wz~j!QNlm{*Pq21K?g$0eH&%qzzQ10vg$=9S}u z0g-LWamnTb^U86-fXFuGxMcHzdF8lZKxCV8T(bGVymDMHAhJz4F4=rwUO6rp5ZR_2 zmux;TuN)T)h-_1iOEw>vSB?t?M7Al%C7Tb-E5`)`BHNVXlFbL^mE(c|k!{Lx$>sy| z%5lMf$TsD;Wb=V}<+xx#WSeqaviZQga$GPVvQ0TI*?eGLIW8Cw*`^$qY(6lr92X3T zY*QvK)sdg@f`QQQc7vfG=+3$+oDKcB7r}P)a0P%c=saRG0Q@}w9XkMEEd~Hyt_A=~ z3II$I?pk%(9_pL#WNXa`d6e&YazZ6WE0K&kba;zKrBY8o!sfVL(HR=66^-%MIGg%d z`{$L)^gl97&`**WpDxaDGOeKexe{nlY{ByaZlte|jBAg3(rWXef(cWB;#liecQ0-M#F{s&gw B2}l3{ literal 0 HcmV?d00001 diff --git a/Editor/Resources/dot.png.meta b/Editor/Resources/dot.png.meta new file mode 100644 index 0000000..7548b64 --- /dev/null +++ b/Editor/Resources/dot.png.meta @@ -0,0 +1,90 @@ +fileFormatVersion: 2 +guid: 78c65cebbe4b15d419856776bf40dedf +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 10 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utilities.meta b/Editor/Utilities.meta new file mode 100644 index 0000000..69c2afd --- /dev/null +++ b/Editor/Utilities.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: becf0f944ae04f689ffd75817e20131d +timeCreated: 1563150449 \ No newline at end of file diff --git a/Editor/Utilities/NodeAssemblies.cs b/Editor/Utilities/NodeAssemblies.cs new file mode 100644 index 0000000..d6c2c5d --- /dev/null +++ b/Editor/Utilities/NodeAssemblies.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public static class NodeAssemblies { + private static Dictionary _dataToNode; + private static Dictionary _stringToData; + + public static Dictionary DataToDisplay => + _dataToNode ?? (_dataToNode = GetDataToDisplay()); + + public static Dictionary StringToData => + _stringToData ?? (_stringToData = GetStringToData()); + + private static Dictionary GetDataToDisplay () { + var dict = new Dictionary(); + + // Expensive to get all assemblies, but only way to get data outside the package + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { + foreach (var type in assembly.GetTypes()) { + if (!type.IsSubclassOf(typeof(NodeEditorBase)) || type.IsAbstract) continue; + + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + + dict.Add(attr.Type, type); + } + } + + return dict; + } + + class TypeEntry { + public Type type; + public string path; + public int priority; + } + + private static Dictionary GetStringToData () { + var list = new List(); + + // Expensive to get all assemblies, but only way to get data outside the package + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { + foreach (var type in assembly.GetTypes()) { + if (!type.IsSubclassOf(typeof(NodeDataBase)) || type.IsAbstract) continue; + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + + list.Add(new TypeEntry { + type = type, + path = attr?.Path ?? type.FullName, + priority = attr?.Priority ?? 0, + }); + } + } + + return list + .OrderByDescending(t => t.priority) + .ToDictionary( + (k) => k.path, + (v) => v.type); + } + } +} diff --git a/Editor/Utilities/NodeAssemblies.cs.meta b/Editor/Utilities/NodeAssemblies.cs.meta new file mode 100644 index 0000000..445c161 --- /dev/null +++ b/Editor/Utilities/NodeAssemblies.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 407417853fc0423185b1f94fb150beed +timeCreated: 1563467022 \ No newline at end of file diff --git a/Editor/Utilities/NodeBoxStyle.cs b/Editor/Utilities/NodeBoxStyle.cs new file mode 100644 index 0000000..0741765 --- /dev/null +++ b/Editor/Utilities/NodeBoxStyle.cs @@ -0,0 +1,56 @@ +using System.Linq; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class NodeBoxStyle { + private GUIStyle _style; + private readonly Color _borderColor; + private readonly Color _backgroundColor; + + private Texture2D _texture; + + public GUIStyle Style { + get { + // Unity hammers the texture on play then stop, rebuild if it vanishes + if (_texture == null) { + CreateTexture(); + } + + if (_style == null) { + _style = new GUIStyle(GUI.skin.box) { + border = new RectOffset(3, 3, 2, 2), + }; + } + + if (_style.normal.background == GUI.skin.box.normal.background + || _style.normal.background == null) { + _style.normal.background = _texture; + } + + return _style; + } + } + + public NodeBoxStyle (Color32 border, Color background) { + _borderColor = border; + _backgroundColor = background; + + CreateTexture(); + } + + private void CreateTexture () { + _texture = new Texture2D(19, 19, TextureFormat.ARGB32, false); + _texture.filterMode = FilterMode.Point; + + // Create an array for border colors with semi-transparency + Color[] borderColors = Enumerable.Repeat(new Color(_borderColor.r, _borderColor.g, _borderColor.b, 128), 19 * 19).ToArray(); + _texture.SetPixels(borderColors); + + // Set the internal area to a transparent background color + Color[] backgroundColors = Enumerable.Repeat(new Color(_backgroundColor.r, _backgroundColor.g, _backgroundColor.b, 0.5f), 17 * 17).ToArray(); + _texture.SetPixels(1, 1, 17, 17, backgroundColors); + + _texture.Apply(); + } + } +} diff --git a/Editor/Utilities/NodeBoxStyle.cs.meta b/Editor/Utilities/NodeBoxStyle.cs.meta new file mode 100644 index 0000000..34ddfd9 --- /dev/null +++ b/Editor/Utilities/NodeBoxStyle.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 174fa15f88784a80adae8ec71d3b8b92 +timeCreated: 1563150466 \ No newline at end of file diff --git a/Editor/Utilities/RectCleaner.cs b/Editor/Utilities/RectCleaner.cs new file mode 100644 index 0000000..f1b2194 --- /dev/null +++ b/Editor/Utilities/RectCleaner.cs @@ -0,0 +1,21 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public static class RectCleaner { + public static Rect FixNegativeSize (this Rect rectOld) { + var rect = new Rect(rectOld); + + if (rect.width < 0) { + rect.x += rect.width; + rect.width = Mathf.Abs(rect.width); + } + + if (rect.height < 0) { + rect.y += rect.height; + rect.height = Mathf.Abs(rect.height); + } + + return rect; + } + } +} diff --git a/Editor/Utilities/RectCleaner.cs.meta b/Editor/Utilities/RectCleaner.cs.meta new file mode 100644 index 0000000..9d09891 --- /dev/null +++ b/Editor/Utilities/RectCleaner.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ed1bd38a267e44eeaac65729c75cfe81 +timeCreated: 1563655588 \ No newline at end of file diff --git a/Editor/Utilities/ThemeUtility.cs b/Editor/Utilities/ThemeUtility.cs new file mode 100644 index 0000000..1f90f43 --- /dev/null +++ b/Editor/Utilities/ThemeUtility.cs @@ -0,0 +1,7 @@ +using UnityEditor; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public static class ThemeUtility { + public static bool IsDarkTheme => EditorGUIUtility.isProSkin; + } +} diff --git a/Editor/Utilities/ThemeUtility.cs.meta b/Editor/Utilities/ThemeUtility.cs.meta new file mode 100644 index 0000000..87b37be --- /dev/null +++ b/Editor/Utilities/ThemeUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6046c9915ae2480aac0ed254866ec23f +timeCreated: 1713807421 \ No newline at end of file diff --git a/Editor/Windows.meta b/Editor/Windows.meta new file mode 100644 index 0000000..55619c4 --- /dev/null +++ b/Editor/Windows.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1f75f9c15b5e4b54b01e5a8d9fd9bf84 +timeCreated: 1563160575 \ No newline at end of file diff --git a/Editor/Windows/DialogueWindow.cs b/Editor/Windows/DialogueWindow.cs new file mode 100644 index 0000000..d518803 --- /dev/null +++ b/Editor/Windows/DialogueWindow.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public interface IDialogueWindow { + Dictionary DataToNode { get; } + } + + public partial class DialogueWindow : EditorWindow, IDialogueWindow { + private readonly List _graveyard = new List(); + + private bool IsGraphPopulated => Nodes != null; + private bool NodesOutOfSync => Nodes.Count != Graph.Nodes.Count; + public List Nodes { get; private set; } + public GraphCrud GraphCrud { get; private set; } + public InputController MouseEvents { get; private set; } + + public Dictionary DataToNode { get; } = + new Dictionary(); + + private static DialogueGraph GraphData { get; set; } + public DialogueGraph Graph => GraphData; + + public static void ShowGraph (DialogueGraph graph, bool focus = true) { + var window = GetWindow(false, "Dialogue", focus); + window.SetGraph(graph); + } + + public static void SaveGraph () { + if (GraphData == null) return; + EditorUtility.SetDirty(GraphData); + AssetDatabase.SaveAssets(); + } + + private void SetGraph (DialogueGraph graph) { + GraphCrud = new GraphCrud(graph, this); + BuildNodes(graph); + + GraphData = graph; + MouseEvents = new InputController(this); + SaveGraphToEditor(graph); + } + + private void BuildNodes (DialogueGraph graph) { + DataToNode.Clear(); + + Nodes = graph.Nodes + .Select(i => CreateNodeInstance(i as NodeDataBase)) + .ToList(); + + BuildNodeConnections(); + } + + private void BuildNodeConnections () { + foreach (var node in Nodes) { + foreach (var connection in node.Out) { + connection.Links.RebuildLinks(); + } + } + } + + public NodeEditorBase CreateNodeInstance (NodeDataBase data) { + var displayType = NodeAssemblies.DataToDisplay[data.GetType()]; + var instance = Activator.CreateInstance(displayType) as NodeEditorBase; + if (instance == null) throw new NullReferenceException($"No type found for ${data}"); + + instance.Setup(this, data); + DataToNode[data] = instance; + + return instance; + } + + private void OnGUI () { + if (Graph == null) return; + + if (!IsGraphPopulated) { + SetGraph(Graph); + } + + if (NodesOutOfSync) { + BuildNodes(Graph); + } + + GUI.Label(new Rect(10, 10, 300, 100), $"Dialogue: {Graph.name}", EditorStyles.boldLabel); + + var e = Event.current; + MouseEvents.Scroll.UpdateScrollView(position); + MouseEvents.ProcessCanvasEvent(e); + + foreach (var node in Nodes) { + if (node.IsMemoryLeak) { + GraveyardAdd(node); + continue; + } + + node.Print(); + } + + MouseEvents.PaintSelection(); + + GUI.EndScrollView(); + UpdateGraveyard(); + } + + private void UpdateGraveyard () { + if (_graveyard.Count == 0) return; + + foreach (var item in _graveyard) { + Nodes.Remove(item); + } + + _graveyard.Clear(); + AssetDatabase.SaveAssets(); + } + + public void GraveyardAdd (NodeEditorBase node) { + _graveyard.Add(node); + } + } +} diff --git a/Editor/Windows/DialogueWindow.cs.meta b/Editor/Windows/DialogueWindow.cs.meta new file mode 100644 index 0000000..2b658bf --- /dev/null +++ b/Editor/Windows/DialogueWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2eeab2a823d431a9e0b8f5c1d519b93 +timeCreated: 1563139774 \ No newline at end of file diff --git a/Editor/Windows/DialogueWindowRestore.cs b/Editor/Windows/DialogueWindowRestore.cs new file mode 100644 index 0000000..f8ec60b --- /dev/null +++ b/Editor/Windows/DialogueWindowRestore.cs @@ -0,0 +1,39 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public partial class DialogueWindow { + private const string PREF_ASSET_GUID = "FluidDialogue_CurrentAsset"; + + private static bool HasSavedGraphId => EditorPrefs.HasKey(PREF_ASSET_GUID); + + private static void SaveGraphToEditor (DialogueGraph graph) { + var path = AssetDatabase.GetAssetPath(graph); + var guid = AssetDatabase.AssetPathToGUID(path); + + EditorPrefs.SetString(PREF_ASSET_GUID, guid); + } + + private static DialogueGraph GetGraphFromEditor () { + var guid = EditorPrefs.GetString(PREF_ASSET_GUID); + var graphPath = AssetDatabase.GUIDToAssetPath(guid); + if (graphPath == null) { + EditorPrefs.DeleteKey(PREF_ASSET_GUID); + return null; + } + + return AssetDatabase.LoadAssetAtPath(graphPath); + } + + // @NOTE Save and restore must be done outside of the Window's OnGui loop to prevent crashing layout groups + [UnityEditor.Callbacks.DidReloadScripts] + public static void RestoreSavedGraph () { + if (!EditorPrefs.GetBool(PREF_OPEN, false)) return; + if (!HasSavedGraphId) return; + var saveGraph = GetGraphFromEditor(); + if (saveGraph == null) return; + ShowGraph(saveGraph, false); + } + } +} diff --git a/Editor/Windows/DialogueWindowRestore.cs.meta b/Editor/Windows/DialogueWindowRestore.cs.meta new file mode 100644 index 0000000..7cfee01 --- /dev/null +++ b/Editor/Windows/DialogueWindowRestore.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b1f09aabf04242c286691bbdfd276711 +timeCreated: 1565563865 \ No newline at end of file diff --git a/Editor/Windows/DialogueWindowUndoRedo.cs b/Editor/Windows/DialogueWindowUndoRedo.cs new file mode 100644 index 0000000..3754f41 --- /dev/null +++ b/Editor/Windows/DialogueWindowUndoRedo.cs @@ -0,0 +1,32 @@ +using UnityEditor; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public partial class DialogueWindow { + private const string PREF_OPEN = "FluidDialogue_WindowOpen"; + + private void Awake () { + BindUndoRedoCallback(); + EditorPrefs.SetBool(PREF_OPEN, true); + RestoreSavedGraph(); + } + + private void OnEnable () { + BindUndoRedoCallback(); + EditorPrefs.SetBool(PREF_OPEN, true); + } + + private void OnDestroy () { + Undo.undoRedoPerformed -= UndoDetected; + EditorPrefs.SetBool(PREF_OPEN, false); + } + + private void BindUndoRedoCallback () { + Undo.undoRedoPerformed -= UndoDetected; + Undo.undoRedoPerformed += UndoDetected; + } + + private void UndoDetected () { + Repaint(); + } + } +} diff --git a/Editor/Windows/DialogueWindowUndoRedo.cs.meta b/Editor/Windows/DialogueWindowUndoRedo.cs.meta new file mode 100644 index 0000000..d3fe582 --- /dev/null +++ b/Editor/Windows/DialogueWindowUndoRedo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 19ea8cea0c854f7a97b2b0775892e845 +timeCreated: 1563466051 \ No newline at end of file diff --git a/Editor/Windows/FindReplaceWindow.meta b/Editor/Windows/FindReplaceWindow.meta new file mode 100644 index 0000000..9e18880 --- /dev/null +++ b/Editor/Windows/FindReplaceWindow.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ddae811b66604919862ef0bea11a4216 +timeCreated: 1589337050 \ No newline at end of file diff --git a/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs b/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs new file mode 100644 index 0000000..10d48a0 --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs @@ -0,0 +1,31 @@ +using CleverCrow.Fluid.FindAndReplace.Editors; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class ChoiceSearchResult : IFindResult { + private readonly Object _target; + public string Text { get; } + + public ChoiceSearchResult (string text, Object target) { + _target = target; + Text = text; + } + + public void Show () { + Selection.activeObject = _target;; + } + + public void Replace (int startIndex, int charactersToReplace, string replaceText) { + var beginning = Text.Substring(0, startIndex); + var end = Text.Substring(startIndex + charactersToReplace); + var text = $"{beginning}{replaceText}{end}"; + + // @NOTE Mixed feels on this, seems risky targeting the prop directly + var obj = new SerializedObject(_target); + obj.FindProperty("text").stringValue = text; + obj.ApplyModifiedProperties(); + AssetDatabase.SaveAssets(); + } + } +} diff --git a/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs.meta b/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs.meta new file mode 100644 index 0000000..9f07d59 --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/ChoiceSearchResult.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a3bc25ee8dab47d48ca2e04f90ba5a95 +timeCreated: 1589337932 \ No newline at end of file diff --git a/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs b/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs new file mode 100644 index 0000000..1ac3c9d --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs @@ -0,0 +1,31 @@ +using CleverCrow.Fluid.FindAndReplace.Editors; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class DialogueSearchResult : IFindResult { + private readonly Object _target; + public string Text { get; } + + public DialogueSearchResult (string text, Object target) { + _target = target; + Text = text; + } + + public void Show () { + Selection.activeObject = _target;; + } + + public void Replace (int startIndex, int charactersToReplace, string replaceText) { + var beginning = Text.Substring(0, startIndex); + var end = Text.Substring(startIndex + charactersToReplace); + var text = $"{beginning}{replaceText}{end}"; + + // @NOTE Mixed feels on this, seems risky targeting the dialogue prop directly + var obj = new SerializedObject(_target); + obj.FindProperty("dialogue").stringValue = text; + obj.ApplyModifiedProperties(); + AssetDatabase.SaveAssets(); + } + } +} diff --git a/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs.meta b/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs.meta new file mode 100644 index 0000000..8e66adb --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/DialogueSearchResult.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 999dd9864e144e5ba37ac7a0bf9b8544 +timeCreated: 1589337146 \ No newline at end of file diff --git a/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs b/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs new file mode 100644 index 0000000..53fd7a7 --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.FindAndReplace.Editors; +using UnityEditor; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class FindReplaceWindow : FindReplaceWindowBase { + [MenuItem("Window/Fluid Dialogue/Find Replace")] + public static void ShowFindReplace () { + ShowWindow(); + } + + protected override IFindResult[] GetFindResults (Func IsValid) { + var results = new List(); + + var graphIDs = AssetDatabase.FindAssets("t:DialogueGraph"); + foreach (var graphID in graphIDs) { + var path = AssetDatabase.GUIDToAssetPath(graphID); + var graph = AssetDatabase.LoadAssetAtPath(path); + if (graph == null) continue; + + foreach (var node in graph.Nodes) { + if (IsValid(node.Text ?? "")) { + var result = new DialogueSearchResult(node.Text, node as UnityEngine.Object); + results.Add(result); + } + + foreach (var choice in node.Choices) { + var result = new ChoiceSearchResult(choice.text, choice); + results.Add(result); + } + } + } + + return results.ToArray(); + } + } +} diff --git a/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs.meta b/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs.meta new file mode 100644 index 0000000..8a8e65d --- /dev/null +++ b/Editor/Windows/FindReplaceWindow/FindReplaceWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9b8209640131438399a95b5df55b9af2 +timeCreated: 1589253863 \ No newline at end of file diff --git a/Editor/Windows/FluidDialogueSettings.cs b/Editor/Windows/FluidDialogueSettings.cs new file mode 100644 index 0000000..f2a6ba9 --- /dev/null +++ b/Editor/Windows/FluidDialogueSettings.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class FluidDialogueSettings : ScriptableObject { + private const string ASSET_PATH = "Assets/Resources/FluidDialogueSettings.asset"; + + [Tooltip("Prevents meta data like choices, actions, and conditions from being set to visible in the " + + "nested object Project window view. Keeping set to true is highly recommended.")] + [SerializeField] + private bool _hideNestedNodeData = true; + + public static FluidDialogueSettings Current => GetOrCreateSettings(); + + public bool HideNestedNodeData => _hideNestedNodeData; + + private static FluidDialogueSettings GetOrCreateSettings () { + var settings = AssetDatabase.LoadAssetAtPath(ASSET_PATH); + if (AssetDatabase.LoadAssetAtPath("Assets/Resources") == null) + AssetDatabase.CreateFolder("Assets", "Resources"); + if (settings != null) return settings; + + settings = CreateInstance(); + AssetDatabase.CreateAsset(settings, ASSET_PATH); + AssetDatabase.SaveAssets(); + + return settings; + } + + internal static SerializedObject GetSerializedSettings () { + return new SerializedObject(GetOrCreateSettings()); + } + } + + static class FluidDialogueSettingsIMGUIRegister { + [SettingsProvider] + public static SettingsProvider CreateMyCustomSettingsProvider () { + // First parameter is the path in the Settings window. + // Second parameter is the scope of this setting: it only appears in the Project Settings window. + var provider = new SettingsProvider("Preferences/Node Editor", SettingsScope.User) { + label = "Fluid Dialogue", + keywords = new HashSet(new[] {"fluid", "dialogue"}), + guiHandler = (searchContext) => { + var settings = FluidDialogueSettings.GetSerializedSettings(); + EditorGUILayout.PropertyField(settings.FindProperty("_hideNestedNodeData")); + settings.ApplyModifiedProperties(); + }, + }; + + return provider; + } + } +} diff --git a/Editor/Windows/FluidDialogueSettings.cs.meta b/Editor/Windows/FluidDialogueSettings.cs.meta new file mode 100644 index 0000000..178e97f --- /dev/null +++ b/Editor/Windows/FluidDialogueSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 121992c1b53540e89de5d3c98729688d +timeCreated: 1565556849 \ No newline at end of file diff --git a/Editor/Windows/GraphCrud.cs b/Editor/Windows/GraphCrud.cs new file mode 100644 index 0000000..f3bb89f --- /dev/null +++ b/Editor/Windows/GraphCrud.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class GraphCrud { + private readonly DialogueGraph _graph; + private readonly DialogueWindow _window; + + public GraphCrud (DialogueGraph graph, DialogueWindow window) { + _graph = graph; + _window = window; + } + + public void CreateData (NodeDataBase data, Vector2 position) { + Undo.SetCurrentGroupName("Create node"); + Undo.RecordObject(_graph, "New node"); + + NewNode(data, position); + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + private void NewNode (NodeDataBase data, Vector2 position) { + data.ClearConnectionChildren(); + data.rect.position = position; + _graph.AddNode(data); + AssetDatabase.AddObjectToAsset(data, _graph); + AssetDatabase.SaveAssets(); + + var instance = _window.CreateNodeInstance(data); + _window.Nodes.Add(instance); + + Undo.RegisterCreatedObjectUndo(data, "Create node"); + } + + public void DeleteNode (NodeEditorBase node) { + Undo.SetCurrentGroupName("Delete node"); + Undo.RecordObject(_graph, "Delete node"); + + CleanupNode(node); + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + public void DeleteNode (IEnumerable nodes) { + Undo.SetCurrentGroupName("Delete nodes"); + Undo.RecordObject(_graph, "Delete node"); + + foreach (var node in nodes) { + CleanupNode(node); + } + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + private void CleanupNode (NodeEditorBase node) { + Undo.RecordObject(node.Data, "Delete node"); + + node.CleanConnections(); + _graph.DeleteNode(node.Data); + _window.GraveyardAdd(node); + Undo.DestroyObjectImmediate(node.Data); + + var childObjects = node.Data.enterActions + .Concat(node.Data.exitActions) + .Concat(node.Data.conditions); + + foreach (var scriptableObject in childObjects) { + Undo.DestroyObjectImmediate(scriptableObject); + } + + node.DeleteCleanup(); + } + + public void DuplicateNode (NodeEditorBase node) { + Undo.SetCurrentGroupName("Duplicate node"); + Undo.RecordObject(_graph, "New node"); + + var copy = node.CreateDataCopy(); + NewNode(copy, new Vector2(copy.rect.position.x + 50, copy.rect.position.y + 50)); + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + + public void DuplicateNode (List nodes) { + Undo.SetCurrentGroupName("Duplicate all nodes"); + Undo.RecordObject(_graph, "New node"); + + foreach (var node in nodes) { + var copy = node.CreateDataCopy(); + NewNode(copy, new Vector2(copy.rect.position.x + 50, copy.rect.position.y + 50)); + } + + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + } + } +} diff --git a/Editor/Windows/GraphCrud.cs.meta b/Editor/Windows/GraphCrud.cs.meta new file mode 100644 index 0000000..22c3e5e --- /dev/null +++ b/Editor/Windows/GraphCrud.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e341ba6bbbe94d1f86ba083fb23ae97e +timeCreated: 1564066071 \ No newline at end of file diff --git a/Editor/Windows/UserInput.meta b/Editor/Windows/UserInput.meta new file mode 100644 index 0000000..0ad77f9 --- /dev/null +++ b/Editor/Windows/UserInput.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5a56a51a6fe54a50a788f7306e9870a8 +timeCreated: 1563590352 \ No newline at end of file diff --git a/Editor/Windows/UserInput/DelayedMenu.cs b/Editor/Windows/UserInput/DelayedMenu.cs new file mode 100644 index 0000000..22609ef --- /dev/null +++ b/Editor/Windows/UserInput/DelayedMenu.cs @@ -0,0 +1,16 @@ +using System; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class DelayedMenu { + public Action Display { private get; set; } + + public bool ShowDelayedMenu (Event e) { + if (Display == null || e.type != EventType.Repaint) return false; + + Display.Invoke(); + Display = null; + return true; + } + } +} diff --git a/Editor/Windows/UserInput/DelayedMenu.cs.meta b/Editor/Windows/UserInput/DelayedMenu.cs.meta new file mode 100644 index 0000000..0ecd386 --- /dev/null +++ b/Editor/Windows/UserInput/DelayedMenu.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fead1bb4c0444071bf0e6a94a7bc6f1d +timeCreated: 1563591701 \ No newline at end of file diff --git a/Editor/Windows/UserInput/InputController.cs b/Editor/Windows/UserInput/InputController.cs new file mode 100644 index 0000000..cee04d7 --- /dev/null +++ b/Editor/Windows/UserInput/InputController.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class InputController { + private readonly NodeSelection _selection; + private readonly LeftClickHandler _leftClick; + private readonly RightClickHandler _rightClick; + private readonly DelayedMenu _delayedMenu = new DelayedMenu(); + + public ScrollManager Scroll { get; } + + public InputController (DialogueWindow window) { + Scroll = new ScrollManager(window, window.Graph); + if (Vector2.Distance(window.Graph.scrollPosition, Vector2.one) < 2) { + Scroll.SetViewToRect(window.Graph.root.rect); + } + + + _selection = new NodeSelection(window); + _leftClick = new LeftClickHandler(window, _selection); + _rightClick = new RightClickHandler(window, _selection, Scroll, _delayedMenu); + } + + public void ProcessCanvasEvent (Event e) { + if (_delayedMenu.ShowDelayedMenu(e)) { + return; + } + + _leftClick.Update(e); + _rightClick.Update(e); + } + + public void PaintSelection () { + _selection.PaintSelection(); + } + } +} diff --git a/Editor/Windows/UserInput/InputController.cs.meta b/Editor/Windows/UserInput/InputController.cs.meta new file mode 100644 index 0000000..f8c94e8 --- /dev/null +++ b/Editor/Windows/UserInput/InputController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5edce5eec0914c4a8b68fac50b2b4485 +timeCreated: 1563160601 \ No newline at end of file diff --git a/Editor/Windows/UserInput/LeftClickHandler.cs b/Editor/Windows/UserInput/LeftClickHandler.cs new file mode 100644 index 0000000..9d60ad1 --- /dev/null +++ b/Editor/Windows/UserInput/LeftClickHandler.cs @@ -0,0 +1,192 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class LeftClickHandler { + private readonly DialogueWindow _window; + private readonly NodeSelection _selection; + + private NodeEditorBase _clickedNode; + private bool _selectingArea; + private bool _isDraggingNode; + private Connection _connection; + private bool _toggleSelectionMode; + + public LeftClickHandler (DialogueWindow window, NodeSelection selection) { + _window = window; + _selection = selection; + } + + public void Update (Event e) { + if (e.button != 0) return; + + if (e.type == EventType.MouseDown) { + _connection = null; + _clickedNode = null; + + foreach (var node in _window.Nodes) { + var connection = node.GetConnection(e.mousePosition); + if (connection != null) { + _connection = connection; + _clickedNode = node; + continue; + } + + if (node.IsHeaderPosition(e.mousePosition)) { + _connection = null; + _clickedNode = node; + } + } + } + + HandleCtrlClick(e); + if (_toggleSelectionMode) { + _clickedNode = null; + return; + } + + if (_connection != null) { + UpdateClickedConnection(e); + } else if (_clickedNode == null) { + DrawSelectionArea(e); + } else if (_clickedNode != null) { + UpdateClickedNode(e); + } + + if (e.type == EventType.MouseUp) { + _clickedNode = null; + } + } + + private void HandleCtrlClick (Event e) { + if (e.keyCode == KeyCode.LeftControl && e.type == EventType.KeyDown) { + _toggleSelectionMode = true; + } + + if (e.keyCode == KeyCode.LeftControl && e.type == EventType.KeyUp) { + _toggleSelectionMode = false; + } + + if (!_toggleSelectionMode) return; + + if (e.type == EventType.MouseDown && _clickedNode != null) { + if (_selection.Contains(_clickedNode)) { + _selection.Remove(_clickedNode); + } else { + _selection.Add(_clickedNode); + } + GUI.changed = true; + } + } + + private void UpdateClickedConnection (Event e) { + switch (e.type) { + case EventType.MouseDrag: + _connection.SetExampleCurve(e.mousePosition); + break; + case EventType.MouseUp: + Connection linkTarget = null; + foreach (var node in _window.Nodes) { + linkTarget = node.GetConnection(e.mousePosition); + if (linkTarget != null) break; + } + + _connection.Links.AddLink(linkTarget); + _connection.ClearCurveExample(); + DialogueWindow.SaveGraph(); + + break; + } + } + + private void DrawSelectionArea (Event e) { + switch (e.type) { + case EventType.MouseDown: + GUI.FocusControl(null); + _selectingArea = true; + _selection.area.position = e.mousePosition; + _selection.area.size = Vector2.zero; + break; + + case EventType.MouseDrag when _selectingArea: + _selection.area.size += e.delta; + break; + + case EventType.MouseUp when _selectingArea: + SetSelection(); + break; + + case EventType.Ignore: + SetSelection(); + break; + } + } + + private void SetSelection () { + var cleanedSelectArea = _selection.area.FixNegativeSize(); + + _selection.RemoveAll(); + var selected = _window.Nodes.Where(n => cleanedSelectArea.Overlaps(n.Data.rect)); + _selection.Add(selected); + + _selection.area.size = Vector2.zero; + _selectingArea = false; + GUI.changed = true; + } + + private void UpdateClickedNode (Event e) { + switch (e.type) { + case EventType.MouseDown when !_selection.Contains(_clickedNode): + _selection.RemoveAll(); + _selection.Add(_clickedNode); + GUI.changed = true; + break; + + case EventType.MouseDrag: + _isDraggingNode = true; + Undo.SetCurrentGroupName("Drag nodes"); + _selection.Selected.ForEach(n => { + Undo.RegisterCompleteObjectUndo(n.Data, "Move node"); + n.Data.rect.position += e.delta; + }); + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + e.Use(); + break; + + case EventType.MouseUp: + if (!_isDraggingNode) { + _selection.RemoveAll(); + _selection.Add(_clickedNode); + GUI.changed = true; + } + + ClearDragging(); + DialogueWindow.SaveGraph(); + break; + + case EventType.Ignore: + ClearDragging(); + DialogueWindow.SaveGraph(); + break; + } + } + + private void ClearDragging () { + if (!_isDraggingNode) return; + + Undo.SetCurrentGroupName("Drag nodes"); + foreach (var node in _selection.Selected) { + foreach (var link in node.In[0].Parents) { + Undo.RegisterCompleteObjectUndo( + link.Data, "Move node"); + link.Data.SortConnectionsByPosition(); + } + } + Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); + + _isDraggingNode = false; + } + } +} diff --git a/Editor/Windows/UserInput/LeftClickHandler.cs.meta b/Editor/Windows/UserInput/LeftClickHandler.cs.meta new file mode 100644 index 0000000..1a07c27 --- /dev/null +++ b/Editor/Windows/UserInput/LeftClickHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 305e746c44aa4231807b23f414310c3a +timeCreated: 1563590388 \ No newline at end of file diff --git a/Editor/Windows/UserInput/NodeSelection.cs b/Editor/Windows/UserInput/NodeSelection.cs new file mode 100644 index 0000000..f99b8b5 --- /dev/null +++ b/Editor/Windows/UserInput/NodeSelection.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class NodeSelection { + private readonly EditorWindow _window; + private readonly NodeBoxStyle _selectBoxStyle = new NodeBoxStyle(Color.black, new Color(0, 0, 0, 0.5f)); + + public Rect area; + + public List Selected { get; } = new List(); + + public NodeSelection (EditorWindow window) { + _window = window; + } + + public void Add (NodeEditorBase node) { + Selected.Add(node); + node.Select(); + Selection.activeObject = node.Data; + } + + public void Add (IEnumerable selected) { + foreach (var node in selected) { + Selected.Add(node); + node.Select(); + } + + Selection.objects = selected.Select(n => n.Data).ToArray(); + } + + public void Remove (NodeEditorBase node) { + Selected.Remove(node); + node.Deselect(); + } + + public void PaintSelection () { + if (!(area.size.magnitude > 1)) return; + + GUI.Box(area, GUIContent.none, _selectBoxStyle.Style); + _window.Repaint(); + } + + public void RemoveAll () { + foreach (var node in Selected.ToList()) { + Remove(node); + } + } + + public bool Contains (NodeEditorBase node) { + return Selected.Contains(node); + } + } +} diff --git a/Editor/Windows/UserInput/NodeSelection.cs.meta b/Editor/Windows/UserInput/NodeSelection.cs.meta new file mode 100644 index 0000000..56f765b --- /dev/null +++ b/Editor/Windows/UserInput/NodeSelection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1cc0c290007b49a3b85af94ef8feb30c +timeCreated: 1563573526 \ No newline at end of file diff --git a/Editor/Windows/UserInput/RightClickHandler.cs b/Editor/Windows/UserInput/RightClickHandler.cs new file mode 100644 index 0000000..4963547 --- /dev/null +++ b/Editor/Windows/UserInput/RightClickHandler.cs @@ -0,0 +1,150 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Editors.NodeDisplays; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class RightClickHandler { + private readonly DialogueWindow _window; + private readonly NodeSelection _selection; + private readonly ScrollManager _scroll; + private readonly DelayedMenu _menu; + + private NodeEditorBase _clickedNode; + private bool _isCameraDragging; + private Connection _connection; + private bool _bodyClick; + + public RightClickHandler ( + DialogueWindow window, + NodeSelection selection, + ScrollManager scroll, + DelayedMenu menu) { + _window = window; + _selection = selection; + _scroll = scroll; + _menu = menu; + } + + public void Update (Event e) { + if (e.button != 1) return; + + if (e.type == EventType.MouseDown) { + _connection = null; + _clickedNode = null; + _bodyClick = false; + + foreach (var node in _window.Nodes) { + var connection = node.GetConnection(e.mousePosition); + if (connection != null) { + _connection = node.GetConnection(e.mousePosition); + _clickedNode = node; + continue; + } + + if (node.IsHeaderPosition(e.mousePosition)) { + _connection = null; + _clickedNode = node; + } else if (node.Data.rect.Contains(e.mousePosition)) { + _bodyClick = true; + } + } + } + + if (_connection != null) { + GUI.FocusControl(null); + ConnectionContextClick(e); + } else if (_clickedNode == null && !_bodyClick) { + GUI.FocusControl(null); + EmptyContextClick(e); + } else if (_clickedNode != null) { + GUI.FocusControl(null); + NodeContextClick(e); + } + + if (e.type == EventType.MouseUp) { + _clickedNode = null; + } + } + + private void ConnectionContextClick (Event e) { + switch (e.type) { + case EventType.MouseUp when _connection.IsClicked(e.mousePosition): + _connection.ShowContextMenu(); + break; + } + } + + private void NodeContextClick (Event e) { + switch (e.type) { + case EventType.MouseDown when !_selection.Contains(_clickedNode): + _selection.RemoveAll(); + _selection.Add(_clickedNode); + GUI.changed = true; + break; + case EventType.MouseUp when _selection.Selected.Count == 1: + _clickedNode.ShowContextMenu(); + break; + case EventType.MouseUp: + ShowEditGroupMenu(e); + break; + } + } + + private void EmptyContextClick (Event e) { + switch (e.type) { + case EventType.MouseDrag: + _scroll.ScrollPos -= e.delta; + _isCameraDragging = true; + e.Use(); + break; + + case EventType.MouseUp: { + var wasDragging = _isCameraDragging; + _isCameraDragging = false; + + if (wasDragging) break; + + _selection.RemoveAll(); + GUI.changed = true; + _menu.Display = () => { ShowCanvasContextMenu(e); }; + + break; + } + } + } + + private void ShowEditGroupMenu (Event e) { + if (_selection.Selected.Any(i => i.Protected)) return; + + var menu = new GenericMenu(); + menu.AddItem(new GUIContent("Duplicate All"), false, () => { + _window.GraphCrud.DuplicateNode(_selection.Selected); + }); + menu.AddItem(new GUIContent("Delete All"), false, () => { + _window.GraphCrud.DeleteNode(_selection.Selected); + }); + + menu.ShowAsContext(); + } + + private void ShowCanvasContextMenu (Event e) { + var menu = new GenericMenu(); + var mousePosition = e.mousePosition; + foreach (var menuLine in NodeAssemblies.StringToData) { + menu.AddItem(new GUIContent(menuLine.Key), false, () => { + var data = ScriptableObject.CreateInstance(menuLine.Value); + _window.GraphCrud.CreateData(data as NodeDataBase, mousePosition); + }); + } + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Show Root"), false, () => { + _scroll.SetViewToRect(_window.Graph.root.rect); + }); + + menu.ShowAsContext(); + } + } +} diff --git a/Editor/Windows/UserInput/RightClickHandler.cs.meta b/Editor/Windows/UserInput/RightClickHandler.cs.meta new file mode 100644 index 0000000..d29947a --- /dev/null +++ b/Editor/Windows/UserInput/RightClickHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cc2205c7334440729abeab9783723364 +timeCreated: 1563590854 \ No newline at end of file diff --git a/Editor/Windows/UserInput/ScrollManager.cs b/Editor/Windows/UserInput/ScrollManager.cs new file mode 100644 index 0000000..f14aa00 --- /dev/null +++ b/Editor/Windows/UserInput/ScrollManager.cs @@ -0,0 +1,41 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEditor; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Editors { + public class ScrollManager { + public const float WINDOW_SIZE = 100000; + + private readonly EditorWindow _window; + private readonly DialogueGraph _graph; + + public Vector2 ScrollPos { + get => _graph.scrollPosition; + set => _graph.scrollPosition = value; + } + + public Rect ScrollRect { get; private set; } + + public ScrollManager (EditorWindow window, DialogueGraph graph) { + _graph = graph; + _window = window; + } + + public void UpdateScrollView (Rect position) { + ScrollPos = GUI.BeginScrollView( + new Rect(0, 0, position.width, position.height), + ScrollPos, + new Rect(0, 0, WINDOW_SIZE, WINDOW_SIZE)); + + ScrollRect = new Rect(ScrollPos, position.size); + } + + public void SetViewToRect (Rect rect) { + var offsetPosition = rect.position; + offsetPosition.x += rect.width / 2 - _window.position.width / 2; + offsetPosition.y += rect.height / 2 - _window.position.height / 2; + + ScrollPos = offsetPosition; + } + } +} diff --git a/Editor/Windows/UserInput/ScrollManager.cs.meta b/Editor/Windows/UserInput/ScrollManager.cs.meta new file mode 100644 index 0000000..0a5c5fa --- /dev/null +++ b/Editor/Windows/UserInput/ScrollManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 52fd4dc4f14144809c45966cf9db40e4 +timeCreated: 1563591337 \ No newline at end of file diff --git a/Editor/com.fluid.dialogue.Editor.asmdef b/Editor/com.fluid.dialogue.Editor.asmdef new file mode 100644 index 0000000..04acafc --- /dev/null +++ b/Editor/com.fluid.dialogue.Editor.asmdef @@ -0,0 +1,19 @@ +{ + "name": "com.fluid.dialogue.Editor", + "references": [ + "com.fluid.dialogue", + "com.fluid.simple-spellcheck.Editor", + "com.fluid.find-and-replace.Editor" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/com.fluid.dialogue.Editor.asmdef.meta b/Editor/com.fluid.dialogue.Editor.asmdef.meta new file mode 100644 index 0000000..ebace10 --- /dev/null +++ b/Editor/com.fluid.dialogue.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 28120fcf46ba03248910da6e1bdfbcd3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..08ccd1a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ +Edit LICENSE.md in root, contents will be replaced. diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..5f44d4d --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6ce8dcabd2fefda4e95a72d35ddd2782 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f05ff9 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Edit README.md in root, contents will be replaced. diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..7293fab --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b899e3e4a30441741b6fd6703ca2e5d7 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime.meta b/Runtime.meta new file mode 100644 index 0000000..2bea624 --- /dev/null +++ b/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 07f395f5f5de51c4792d3e0818c221ca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Actions.meta b/Runtime/Actions.meta new file mode 100644 index 0000000..f67172d --- /dev/null +++ b/Runtime/Actions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4d8da7f11a4a4a1f9e0c24adfa973148 +timeCreated: 1560803066 \ No newline at end of file diff --git a/Runtime/Actions/ActionDataBase.cs b/Runtime/Actions/ActionDataBase.cs new file mode 100644 index 0000000..75ca43b --- /dev/null +++ b/Runtime/Actions/ActionDataBase.cs @@ -0,0 +1,22 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Actions { + public abstract class ActionDataBase : NodeNestedDataBase, IActionData { + public virtual void OnInit (IDialogueController dialogue) {} + + public virtual void OnStart () {} + + public virtual ActionStatus OnUpdate () { + return ActionStatus.Success; + } + + public virtual void OnExit () {} + + public virtual void OnReset () {} + + public override IAction GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new ActionRuntime(dialogue, _uniqueId, Instantiate(this)); + } + } +} diff --git a/Runtime/Actions/ActionDataBase.cs.meta b/Runtime/Actions/ActionDataBase.cs.meta new file mode 100644 index 0000000..7838812 --- /dev/null +++ b/Runtime/Actions/ActionDataBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b3048aeb103642c988d7b75038bdfc42 +timeCreated: 1561053773 \ No newline at end of file diff --git a/Runtime/Actions/ActionRuntime.cs b/Runtime/Actions/ActionRuntime.cs new file mode 100644 index 0000000..f771219 --- /dev/null +++ b/Runtime/Actions/ActionRuntime.cs @@ -0,0 +1,77 @@ +namespace CleverCrow.Fluid.Dialogues.Actions { + public interface IActionData { + void OnInit (IDialogueController dialogue); + void OnStart (); + ActionStatus OnUpdate (); + void OnExit (); + void OnReset (); + } + + public class ActionRuntime : IAction { + private readonly IDialogueController _dialogueController; + private readonly IActionData _data; + + private bool _initUsed; + private bool _startUsed; + private bool _resetReady; + private bool _active; + + public string UniqueId { get; } + + public ActionRuntime (IDialogueController dialogue, string uniqueId, IActionData data) { + _data = data; + _dialogueController = dialogue; + UniqueId = uniqueId; + } + + public ActionStatus Tick () { + Reset(); + Init(); + Start(); + + var status = Update(); + if (status == ActionStatus.Success) { + Exit(); + } + + return status; + } + + public void End () { + if (_active) Exit(); + } + + private void Init () { + if (_initUsed) return; + _initUsed = true; + + _data.OnInit(_dialogueController); + } + + private void Start () { + if (_startUsed) return; + _startUsed = true; + _active = true; + + _data.OnStart(); + } + + private void Exit () { + _startUsed = false; + _resetReady = true; + _active = false; + + _data.OnExit(); + } + + private ActionStatus Update () { + return _data.OnUpdate(); + } + + private void Reset () { + if (!_resetReady) return; + _resetReady = false; + _data.OnReset(); + } + } +} diff --git a/Runtime/Actions/ActionRuntime.cs.meta b/Runtime/Actions/ActionRuntime.cs.meta new file mode 100644 index 0000000..8874d8f --- /dev/null +++ b/Runtime/Actions/ActionRuntime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c4c25841aa24239b837a5fde8c2907b +timeCreated: 1560977954 \ No newline at end of file diff --git a/Runtime/Actions/ActionStatus.cs b/Runtime/Actions/ActionStatus.cs new file mode 100644 index 0000000..8704cd7 --- /dev/null +++ b/Runtime/Actions/ActionStatus.cs @@ -0,0 +1,6 @@ +namespace CleverCrow.Fluid.Dialogues.Actions { + public enum ActionStatus { + Success, + Continue + } +} diff --git a/Runtime/Actions/ActionStatus.cs.meta b/Runtime/Actions/ActionStatus.cs.meta new file mode 100644 index 0000000..6b805ff --- /dev/null +++ b/Runtime/Actions/ActionStatus.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1bf680d4dea94149ad920feee4aba08a +timeCreated: 1560803078 \ No newline at end of file diff --git a/Runtime/Actions/IAction.cs b/Runtime/Actions/IAction.cs new file mode 100644 index 0000000..7ed5706 --- /dev/null +++ b/Runtime/Actions/IAction.cs @@ -0,0 +1,6 @@ +namespace CleverCrow.Fluid.Dialogues.Actions { + public interface IAction : IUniqueId { + ActionStatus Tick (); + void End (); + } +} diff --git a/Runtime/Actions/IAction.cs.meta b/Runtime/Actions/IAction.cs.meta new file mode 100644 index 0000000..170c9c6 --- /dev/null +++ b/Runtime/Actions/IAction.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f272b570cb2c4eecb1b931969429304a +timeCreated: 1560709972 \ No newline at end of file diff --git a/Runtime/Actions/Libraries.meta b/Runtime/Actions/Libraries.meta new file mode 100644 index 0000000..23272aa --- /dev/null +++ b/Runtime/Actions/Libraries.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a27cda50014f446a0a50030c73c14ee1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Actions/Libraries/Databases.meta b/Runtime/Actions/Libraries/Databases.meta new file mode 100644 index 0000000..7c16fc4 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d746a9419703445c89031ea3ebaeea9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Actions/Libraries/Databases/Globals.meta b/Runtime/Actions/Libraries/Databases/Globals.meta new file mode 100644 index 0000000..45513f2 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fb2b3174251248ad8844bb3b822d222b +timeCreated: 1609262703 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions.meta b/Runtime/Actions/Libraries/Databases/Globals/Actions.meta new file mode 100644 index 0000000..dbaa4f3 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 35d0c1ff12134c9e9480caa16d3b893e +timeCreated: 1609262790 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs new file mode 100644 index 0000000..6f90384 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Globals/Set Bool")] + public class SetGlobalBool : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionBool _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Bools; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs.meta new file mode 100644 index 0000000..140eacf --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalBool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dc39c8b552b94c01a6ae86d4b8cce494 +timeCreated: 1609262759 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs new file mode 100644 index 0000000..8eaab54 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Globals/Set Float")] + public class SetGlobalFloat : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionFloat _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Floats; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs.meta new file mode 100644 index 0000000..45ed662 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalFloat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a69455b17de54650a8635283e7eaf328 +timeCreated: 1609262759 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs new file mode 100644 index 0000000..387f622 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Globals/Set Int")] + public class SetGlobalInt : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionInt _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Ints; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs.meta new file mode 100644 index 0000000..2cf41e7 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalInt.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ddc85cb2034d4fbb87ac9531677c4ce4 +timeCreated: 1609262759 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs new file mode 100644 index 0000000..bcf42f9 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Globals/Set String")] + public class SetGlobalString : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionString _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Strings; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs.meta new file mode 100644 index 0000000..6ef5157 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Actions/SetGlobalString.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 41def35ef7ec4ad7ab95a8df4a3ca85a +timeCreated: 1609262759 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions.meta b/Runtime/Actions/Libraries/Databases/Globals/Conditions.meta new file mode 100644 index 0000000..df18017 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b7b6e150066c4d4b808c7b40a1ca125d +timeCreated: 1609263063 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs new file mode 100644 index 0000000..6c25754 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs @@ -0,0 +1,11 @@ +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Actions.Databases; + +namespace CleverCrow.Fluid.Dialogues.Conditions.Databases { + [CreateMenu("Database/Globals/Is Bool")] + public class IsGlobalBool : IsBoolBase { + protected override IKeyValueData GetBoolInstance (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Bools; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs.meta new file mode 100644 index 0000000..95f02a8 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalBool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a21642593dc640d08d902334f670b149 +timeCreated: 1609263100 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs new file mode 100644 index 0000000..cd0785f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs @@ -0,0 +1,11 @@ +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Actions.Databases; + +namespace CleverCrow.Fluid.Dialogues.Conditions.Databases { + [CreateMenu("Database/Globals/Is Float")] + public class IsGlobalFloat : IsFloatBase { + protected override IKeyValueData GetFloatInstance (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Floats; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs.meta new file mode 100644 index 0000000..2c32f0f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalFloat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a61e32b19da4eafab4cd1bf3f00b1d0 +timeCreated: 1609265273 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs new file mode 100644 index 0000000..ad32b4f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs @@ -0,0 +1,11 @@ +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Actions.Databases; + +namespace CleverCrow.Fluid.Dialogues.Conditions.Databases { + [CreateMenu("Database/Globals/Is Int")] + public class IsGlobalInt : IsIntBase { + protected override IKeyValueData GetIntInstance (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Ints; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs.meta new file mode 100644 index 0000000..5aef5c3 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalInt.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3b8de77378d4aab95e9a13438e66689 +timeCreated: 1609265223 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs new file mode 100644 index 0000000..bb80365 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs @@ -0,0 +1,11 @@ +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Actions.Databases; + +namespace CleverCrow.Fluid.Dialogues.Conditions.Databases { + [CreateMenu("Database/Globals/Is String")] + public class IsGlobalString : IsStringBase { + protected override IKeyValueData GetStringInstance (IDialogueController dialogue) { + return GlobalDatabaseManager.Instance.Database.Strings; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs.meta b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs.meta new file mode 100644 index 0000000..5d8b6e8 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Globals/Conditions/IsGlobalString.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59c15c982b88481780c0df4b164b2119 +timeCreated: 1609265394 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals.meta b/Runtime/Actions/Libraries/Databases/Locals.meta new file mode 100644 index 0000000..78d4d5c --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d1bf8dc036924b5bb43f2f1faafeb4f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions.meta new file mode 100644 index 0000000..b4fe94a --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b89cc8e67724869a13172db8645a12e +timeCreated: 1561943496 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs new file mode 100644 index 0000000..e07cf2f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Set Bool")] + public class SetLocalBool : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionBool _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return dialogue.LocalDatabase.Bools; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs.meta new file mode 100644 index 0000000..64046db --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalBool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a708f890ba314c06a22a6ea611b7447 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs new file mode 100644 index 0000000..a553f75 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Set Float")] + public class SetLocalFloat : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionFloat _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return dialogue.LocalDatabase.Floats; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs.meta new file mode 100644 index 0000000..fafd7f6 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalFloat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d546e570e5814a63b21d5b8dc536991d +timeCreated: 1561943043 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs new file mode 100644 index 0000000..b05fdb3 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Set Int")] + public class SetLocalInt : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionInt _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return dialogue.LocalDatabase.Ints; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs.meta new file mode 100644 index 0000000..cd0245f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalInt.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a372e8103ec14605908dc5f5d892b795 +timeCreated: 1561943000 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs new file mode 100644 index 0000000..1835aa8 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs @@ -0,0 +1,16 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Set String")] + public class SetLocalString : SetLocalVariableBase { + [SerializeField] + public KeyValueDefinitionString _variable; + + protected override KeyValueDefinitionBase Variable => _variable; + + protected override IKeyValueData GetDatabase (IDialogueController dialogue) { + return dialogue.LocalDatabase.Strings; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs.meta new file mode 100644 index 0000000..5338641 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalString.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 353a61f903c6489d8a0c80aa34771bef +timeCreated: 1561942453 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs new file mode 100644 index 0000000..bbb2960 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs @@ -0,0 +1,37 @@ +using CleverCrow.Fluid.Databases; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public abstract class SetLocalVariableBase : ActionDataBase { + private SetKeyValueInternal _setKeyValue; + + [SerializeField] + public T _value; + + protected abstract KeyValueDefinitionBase Variable { get; } + + public override void OnInit (IDialogueController controller) { + _setKeyValue = new SetKeyValueInternal(GetDatabase(controller)); + } + + public override ActionStatus OnUpdate () { + _setKeyValue.WriteValue(Variable.key, _value); + + return base.OnUpdate(); + } + + protected abstract IKeyValueData GetDatabase (IDialogueController dialogue); + } + + public class SetKeyValueInternal { + private readonly IKeyValueData _database; + + public SetKeyValueInternal (IKeyValueData database) { + _database = database; + } + + public void WriteValue (string key, T value) { + _database.Set(key, value); + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs.meta new file mode 100644 index 0000000..61d3944 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Actions/SetLocalVariableBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d9bf3fb61224d959c0ea8e290553d64 +timeCreated: 1561942577 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions.meta new file mode 100644 index 0000000..e29f899 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8b9d1af096494740b01e4b4c15d6c3c2 +timeCreated: 1561943512 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool.meta new file mode 100644 index 0000000..ababa65 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9ad7324121844d6191fe34899e5dc332 +timeCreated: 1609263218 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs new file mode 100644 index 0000000..8565f3e --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs @@ -0,0 +1,62 @@ +using System; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public abstract class IsBoolBase : ConditionDataBase { + private ConditionBoolInternal _condition; + + [SerializeField] + private KeyValueDefinitionBool _variable = null; + + [SerializeField] + private Comparison _comparison = Comparison.Equal; + + [SerializeField] + private bool _value = true; + + private enum Comparison { + Equal, + NotEqual + } + + protected abstract IKeyValueData GetBoolInstance (IDialogueController dialogue); + + public override void OnInit (IDialogueController dialogue) { + _condition = new ConditionBoolInternal(GetBoolInstance(dialogue)); + } + + public override bool OnGetIsValid (INode parent) { + switch (_comparison) { + case Comparison.Equal: + return _condition.AreValuesEqual(_variable, _value); + case Comparison.NotEqual: + return _condition.AreValuesNotEqual(_variable, _value); + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + public class ConditionBoolInternal { + private readonly IKeyValueData _database; + + public ConditionBoolInternal (IKeyValueData database) { + _database = database; + } + + public bool AreValuesEqual (IKeyValueDefinition definition, bool value) { + var kvp = _database.Get(definition.Key, definition.DefaultValue); + + return kvp == value; + } + + public bool AreValuesNotEqual (IKeyValueDefinition definition, bool value) { + var kvp = _database.Get(definition.Key, definition.DefaultValue); + + return kvp != value; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs.meta new file mode 100644 index 0000000..ab11b3b --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsBoolBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 61a8ec699cc043048888b83ea1fa8a76 +timeCreated: 1609263240 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs new file mode 100644 index 0000000..20ec97e --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs @@ -0,0 +1,10 @@ +using CleverCrow.Fluid.Databases; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Is Bool")] + public class IsLocalBool : IsBoolBase { + protected override IKeyValueData GetBoolInstance (IDialogueController dialogue) { + return dialogue.LocalDatabase.Bools; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs.meta new file mode 100644 index 0000000..4f65855 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalBool/IsLocalBool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4cca882da5bb4218bd3a1b8f82560fd9 +timeCreated: 1561943534 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat.meta new file mode 100644 index 0000000..3442e0a --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c6458136a4df46e2b5f13866dfb98d96 +timeCreated: 1609264213 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs new file mode 100644 index 0000000..8cd279f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs @@ -0,0 +1,61 @@ +using System; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Is Float")] + public abstract class IsFloatBase : ConditionDataBase { + private ConditionFloatInternal _condition; + + [SerializeField] + private KeyValueDefinitionFloat _variable = null; + + [SerializeField] + private NumberComparison _comparison = NumberComparison.Equal; + + [SerializeField] + private float _value = 0; + + protected abstract IKeyValueData GetFloatInstance (IDialogueController dialogue); + + public override void OnInit (IDialogueController dialogue) { + _condition = new ConditionFloatInternal(GetFloatInstance(dialogue)); + } + + public override bool OnGetIsValid (INode parent) { + return _condition.IsComparisonValid(_variable, _value, _comparison); + } + } + + public class ConditionFloatInternal { + private readonly IKeyValueData _database; + + public ConditionFloatInternal (IKeyValueData database) { + _database = database; + } + + public bool IsComparisonValid (IKeyValueDefinition definition, float value, + NumberComparison comparison) { + var dbValue = _database.Get(definition.Key, definition.DefaultValue); + + switch (comparison) { + case NumberComparison.Equal: + return Math.Abs(dbValue - value) < 0.01f; + case NumberComparison.NotEqual: + return Math.Abs(dbValue - value) > 0.01f; + case NumberComparison.GreaterThan: + return value > dbValue; + case NumberComparison.GreaterThanOrEqual: + return value >= dbValue; + case NumberComparison.LessThan: + return value < dbValue; + case NumberComparison.LessThanOrEqual: + return value <= dbValue; + default: + throw new ArgumentOutOfRangeException(nameof(comparison), comparison, null); + } + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs.meta new file mode 100644 index 0000000..d9a0517 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsFloatBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5bb41044534a4922a63918dbdd85c24e +timeCreated: 1609264264 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs new file mode 100644 index 0000000..b2cbd35 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs @@ -0,0 +1,10 @@ +using CleverCrow.Fluid.Databases; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Is Float")] + public class ConditionLocalFloat : IsFloatBase { + protected override IKeyValueData GetFloatInstance (IDialogueController dialogue) { + return dialogue.LocalDatabase.Floats; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs.meta new file mode 100644 index 0000000..dcb8f15 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalFloat/IsLocalFloat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 61438de567d04bcf9aeeb56323cd6e57 +timeCreated: 1562039305 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt.meta new file mode 100644 index 0000000..0d81343 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c4306dea8d74be3ba28a126666bf9ba +timeCreated: 1609264598 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs new file mode 100644 index 0000000..bb3c36b --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs @@ -0,0 +1,59 @@ +using System; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public abstract class IsIntBase : ConditionDataBase { + private ConditionIntInternal _condition; + + [SerializeField] + private KeyValueDefinitionInt _variable = null; + + [SerializeField] + private NumberComparison _comparison = NumberComparison.Equal; + + [SerializeField] + private int _value = 0; + + protected abstract IKeyValueData GetIntInstance (IDialogueController dialogue); + + public override void OnInit (IDialogueController dialogue) { + _condition = new ConditionIntInternal(GetIntInstance(dialogue)); + } + + public override bool OnGetIsValid (INode parent) { + return _condition.IsComparisonValid(_variable, _value, _comparison); + } + } + + public class ConditionIntInternal { + private readonly IKeyValueData _database; + + public ConditionIntInternal (IKeyValueData database) { + _database = database; + } + + public bool IsComparisonValid (IKeyValueDefinition definition, int value, NumberComparison comparison) { + var dbValue = _database.Get(definition.Key, definition.DefaultValue); + + switch (comparison) { + case NumberComparison.Equal: + return dbValue == value; + case NumberComparison.NotEqual: + return dbValue != value; + case NumberComparison.GreaterThan: + return value > dbValue; + case NumberComparison.GreaterThanOrEqual: + return value >= dbValue; + case NumberComparison.LessThan: + return value < dbValue; + case NumberComparison.LessThanOrEqual: + return value <= dbValue; + default: + throw new ArgumentOutOfRangeException(nameof(comparison), comparison, null); + } + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs.meta new file mode 100644 index 0000000..ac1aca3 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsIntBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e889ebfec1f64a338f57cd21f1858583 +timeCreated: 1609264645 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs new file mode 100644 index 0000000..14efffd --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs @@ -0,0 +1,10 @@ +using CleverCrow.Fluid.Databases; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Is Int")] + public class IsLocalInt : IsIntBase { + protected override IKeyValueData GetIntInstance (IDialogueController dialogue) { + return dialogue.LocalDatabase.Ints; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs.meta new file mode 100644 index 0000000..f28c90d --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalInt/IsLocalInt.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 05d3e40f2c4a46ae9df2e1810d976d43 +timeCreated: 1562008210 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString.meta new file mode 100644 index 0000000..6a8af1b --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 140d7cdb799e46509046cc08c65ae702 +timeCreated: 1609264944 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs new file mode 100644 index 0000000..b6a5348 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs @@ -0,0 +1,10 @@ +using CleverCrow.Fluid.Databases; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + [CreateMenu("Database/Locals/Is String")] + public class IsLocalString : IsStringBase { + protected override IKeyValueData GetStringInstance (IDialogueController dialogue) { + return dialogue.LocalDatabase.Strings; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs.meta new file mode 100644 index 0000000..8137a9f --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsLocalString.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8a3e535472bc45ca9a406f30a8fd597c +timeCreated: 1562004831 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs new file mode 100644 index 0000000..c97bef1 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs @@ -0,0 +1,62 @@ +using System; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public abstract class IsStringBase : ConditionDataBase { + private ConditionStringInternal _condition; + + [SerializeField] + private KeyValueDefinitionString _variable = null; + + [SerializeField] + private Comparison _comparison = Comparison.Equal; + + [SerializeField] + private string _value = null; + + private enum Comparison { + Equal, + NotEqual + } + + protected abstract IKeyValueData GetStringInstance (IDialogueController dialogue); + + public override void OnInit (IDialogueController dialogue) { + _condition = new ConditionStringInternal(GetStringInstance(dialogue)); + } + + public override bool OnGetIsValid (INode parent) { + switch (_comparison) { + case Comparison.Equal: + return _condition.AreValuesEqual(_variable, _value); + case Comparison.NotEqual: + return _condition.AreValuesNotEqual(_variable, _value); + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + public class ConditionStringInternal { + private readonly IKeyValueData _database; + + public ConditionStringInternal (IKeyValueData database) { + _database = database; + } + + public bool AreValuesEqual (IKeyValueDefinition definition, string value) { + var kvp = _database.Get(definition.Key, definition.DefaultValue); + + return kvp == value; + } + + public bool AreValuesNotEqual (IKeyValueDefinition definition, string value) { + var kvp = _database.Get(definition.Key, definition.DefaultValue); + + return kvp != value; + } + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs.meta new file mode 100644 index 0000000..7e84ba8 --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/IsLocalString/IsStringBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 05b9854c79cb4a709cc82573f2fc46be +timeCreated: 1609264965 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs b/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs new file mode 100644 index 0000000..c84ddaa --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs @@ -0,0 +1,10 @@ +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public enum NumberComparison { + Equal, + NotEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + } +} diff --git a/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs.meta b/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs.meta new file mode 100644 index 0000000..51b774a --- /dev/null +++ b/Runtime/Actions/Libraries/Databases/Locals/Conditions/NumberComparison.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59282c3df7e44108acc96fa8cc18a13b +timeCreated: 1562008370 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects.meta b/Runtime/Actions/Libraries/GameObjects.meta new file mode 100644 index 0000000..87538b3 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aca6645304124d5f93183b0f41f4d32d +timeCreated: 1585279213 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions.meta b/Runtime/Actions/Libraries/GameObjects/Actions.meta new file mode 100644 index 0000000..bc71c15 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d01904d62a344ea6aeb8f3f23c4f2e5e +timeCreated: 1585279373 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs b/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs new file mode 100644 index 0000000..da06b3f --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Set Active")] + public class ActionSetActive : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private bool _setActive; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SetActive(_setActive); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs.meta new file mode 100644 index 0000000..b51a4dc --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/ActionSetActive.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df570d683f114d44abe229f3daba4dbb +timeCreated: 1585279230 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs b/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs new file mode 100644 index 0000000..46411be --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs @@ -0,0 +1,50 @@ +using System; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + public static class GameObjectUtilities { + public static GameObject FindGameObject (string name) { + var target = GameObject.Find(name); + + if (target == null) { + // Try to find inactive object's parent one level up + // @NOTE Only works if the parent is active for runtime performance reasons + var hasParent = name.Contains("/"); + if (hasParent) { + var parentPath = name.Substring(0, name.LastIndexOf("/", StringComparison.Ordinal)); + var parent = GameObject.Find(parentPath); + + // We know we have a parent, now we need to find the inactive child + if (parent != null) { + var objectName = name.Substring(name.LastIndexOf("/", StringComparison.Ordinal) + 1); + foreach (Transform child in parent.transform) { + if (child.gameObject.name == objectName) { + target = child.gameObject; + break; + } + } + } + } else { + // Look at top level inactive objects only for performance reasons + // @NOTE This only works if the object is in the active scene + var currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); + var rootObjects = currentScene.GetRootGameObjects(); + foreach (var obj in rootObjects) { + if (obj.name == name) { + target = obj; + break; + } + } + } + } + + // Give up and fire an error + if (target == null) { + Debug.LogError($"GameObject not found: {name}. SendMessage action will not run. Make sure the object path is correct.\nIf the object is inactive it must be nested under an active object or in the active scene if using async loading."); + return null; + } + + return target; + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs.meta new file mode 100644 index 0000000..8e898f7 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/GameObjectUtilities.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6e8b757d08544840bad085c69282358e +timeCreated: 1713987162 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage.meta new file mode 100644 index 0000000..9be742f --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c00b7a5f10f347e4b239b3c2c9a3fe26 +timeCreated: 1713986894 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs new file mode 100644 index 0000000..73cac61 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs @@ -0,0 +1,22 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Send Message/Default")] + public class ActionSendMessage : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private string _methodName; + + [SerializeField] + SendMessageOptions _options; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SendMessage(_methodName, _options); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs.meta new file mode 100644 index 0000000..0ede377 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessage.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a4a5f32d02a4826a57d4ca1997744f8 +timeCreated: 1713986914 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs new file mode 100644 index 0000000..e7e2561 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Send Message/Bool")] + public class ActionSendMessageBool : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private string _methodName; + + [SerializeField] + private bool _value; + + [SerializeField] + SendMessageOptions _options; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SendMessage(_methodName, _value, _options); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs.meta new file mode 100644 index 0000000..4fe8463 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageBool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 90924811a5ed4e2e996a1975ca0d2d5a +timeCreated: 1713987645 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs new file mode 100644 index 0000000..0b00058 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Send Message/Float")] + public class ActionSendMessageFloat : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private string _methodName; + + [SerializeField] + private float _value; + + [SerializeField] + SendMessageOptions _options; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SendMessage(_methodName, _value, _options); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs.meta new file mode 100644 index 0000000..1ab9cb5 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageFloat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d4a70d9e108c496095c540b29ba0e1b2 +timeCreated: 1713987659 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs new file mode 100644 index 0000000..c069916 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Send Message/Int")] + public class ActionSendMessageInt : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private string _methodName; + + [SerializeField] + private int _value; + + [SerializeField] + SendMessageOptions _options; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SendMessage(_methodName, _value, _options); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs.meta new file mode 100644 index 0000000..8c0cec6 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageInt.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 946e9b68658d4b7ebcee2d476837a535 +timeCreated: 1713987458 \ No newline at end of file diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs new file mode 100644 index 0000000..e17bec6 --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Actions.GameObjects { + [CreateMenu("GameObject/Send Message/String")] + public class ActionSendMessageString : ActionDataBase { + [SerializeField] + private string _gameObjectName; + + [SerializeField] + private string _methodName; + + [SerializeField] + private string _value; + + [SerializeField] + SendMessageOptions _options; + + public override void OnStart () { + var target = GameObjectUtilities.FindGameObject(_gameObjectName); + if (target == null) return; + + target.SendMessage(_methodName, _value, _options); + } + } +} diff --git a/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs.meta b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs.meta new file mode 100644 index 0000000..12429ec --- /dev/null +++ b/Runtime/Actions/Libraries/GameObjects/Actions/SendMessage/ActionSendMessageString.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 41da87cb052d42618b329e4c4acb77dd +timeCreated: 1713987389 \ No newline at end of file diff --git a/Runtime/Actors.meta b/Runtime/Actors.meta new file mode 100644 index 0000000..f6bae83 --- /dev/null +++ b/Runtime/Actors.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: da45f581fb4c43d2b4a32fc35af586ce +timeCreated: 1561069584 \ No newline at end of file diff --git a/Runtime/Actors/ActorDefinition.cs b/Runtime/Actors/ActorDefinition.cs new file mode 100644 index 0000000..1722f34 --- /dev/null +++ b/Runtime/Actors/ActorDefinition.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + [CreateAssetMenu(fileName = "Actor", menuName = "Fluid/Dialogue/Actor")] + public class ActorDefinition : ScriptableObject, IActor { + [SerializeField] + private string _displayName = null; + + [SerializeField] + private Sprite _portrait = null; + + public string DisplayName => _displayName; + public Sprite Portrait => _portrait; + } +} diff --git a/Runtime/Actors/ActorDefinition.cs.meta b/Runtime/Actors/ActorDefinition.cs.meta new file mode 100644 index 0000000..037a81b --- /dev/null +++ b/Runtime/Actors/ActorDefinition.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 604a6ace6b46473fa9efde504ee00623 +timeCreated: 1561069638 \ No newline at end of file diff --git a/Runtime/Actors/IActor.cs b/Runtime/Actors/IActor.cs new file mode 100644 index 0000000..654938c --- /dev/null +++ b/Runtime/Actors/IActor.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + public interface IActor { + string DisplayName { get; } + Sprite Portrait { get; } + } +} diff --git a/Runtime/Actors/IActor.cs.meta b/Runtime/Actors/IActor.cs.meta new file mode 100644 index 0000000..a61dd99 --- /dev/null +++ b/Runtime/Actors/IActor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea346bde8738471eb99c7b250071adc2 +timeCreated: 1560536031 \ No newline at end of file diff --git a/Runtime/Attributes.meta b/Runtime/Attributes.meta new file mode 100644 index 0000000..91a887d --- /dev/null +++ b/Runtime/Attributes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5b304ba21c6043ceb0b47645e4f21fdf +timeCreated: 1564950285 \ No newline at end of file diff --git a/Runtime/Attributes/CreateMenuAttribute.cs b/Runtime/Attributes/CreateMenuAttribute.cs new file mode 100644 index 0000000..3f849a7 --- /dev/null +++ b/Runtime/Attributes/CreateMenuAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace CleverCrow.Fluid.Dialogues { + public class CreateMenuAttribute : Attribute { + public string Path { get; } + public int Priority { get; } + + public CreateMenuAttribute (string path, int priority = 0) { + Path = path; + Priority = priority; + } + } +} diff --git a/Runtime/Attributes/CreateMenuAttribute.cs.meta b/Runtime/Attributes/CreateMenuAttribute.cs.meta new file mode 100644 index 0000000..0c8ea22 --- /dev/null +++ b/Runtime/Attributes/CreateMenuAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 715d11b0571e4734bc61a90cc6951961 +timeCreated: 1564536320 \ No newline at end of file diff --git a/Runtime/Choices.meta b/Runtime/Choices.meta new file mode 100644 index 0000000..17f0fb0 --- /dev/null +++ b/Runtime/Choices.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d54de3a27fb465bb6902c202f2277c4 +timeCreated: 1560975720 \ No newline at end of file diff --git a/Runtime/Choices/ChoiceData.cs b/Runtime/Choices/ChoiceData.cs new file mode 100644 index 0000000..abb82bb --- /dev/null +++ b/Runtime/Choices/ChoiceData.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Choices { + public class ChoiceData : ScriptableObject, IGetRuntime, IConnectionChildCollection { + public string text; + + [HideInInspector] + public List children = new List(); + + [HideInInspector] + [SerializeField] + private string _uniqueId; + + public string UniqueId => _uniqueId; + + public IReadOnlyList Children => children; + + public void Setup () { + name = "Choice"; + _uniqueId = Guid.NewGuid().ToString(); + } + + public IChoice GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new ChoiceRuntime( + graphRuntime, + text, + _uniqueId, + children.ToList()); + } + + public void AddConnectionChild (NodeDataBase child) { + children.Add(child); + } + + public void RemoveConnectionChild (NodeDataBase child) { + children.Remove(child); + } + + public void SortConnectionsByPosition () { + children = children.OrderBy(i => i.rect.yMin).ToList(); + } + + public void ClearConnectionChildren () { + children.Clear(); + } + } +} diff --git a/Runtime/Choices/ChoiceData.cs.meta b/Runtime/Choices/ChoiceData.cs.meta new file mode 100644 index 0000000..de4dff6 --- /dev/null +++ b/Runtime/Choices/ChoiceData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fbb407621a43446dac370b409a9ddc29 +timeCreated: 1561065282 \ No newline at end of file diff --git a/Runtime/Choices/ChoiceRuntime.cs b/Runtime/Choices/ChoiceRuntime.cs new file mode 100644 index 0000000..03b97a6 --- /dev/null +++ b/Runtime/Choices/ChoiceRuntime.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Choices { + public class ChoiceRuntime : IChoice { + private readonly IGraph _runtime; + private readonly List _children; + private List _childrenRuntimeCache; + + public string UniqueId { get; } + public string Text { get; } + public bool IsValid => Children.Count == 0 || Children.Find(c => c.IsValid) != null; + + private List Children => + _childrenRuntimeCache ?? + (_childrenRuntimeCache = _children.Select(_runtime.GetCopy).ToList()); + + public ChoiceRuntime (IGraph runtime, string text, string uniqueId, List children) { + _runtime = runtime; + Text = text; + UniqueId = uniqueId; + _children = children; + } + + public INode GetValidChildNode () { + return Children.Find(c => c.IsValid); + } + } +} diff --git a/Runtime/Choices/ChoiceRuntime.cs.meta b/Runtime/Choices/ChoiceRuntime.cs.meta new file mode 100644 index 0000000..3c3bba3 --- /dev/null +++ b/Runtime/Choices/ChoiceRuntime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0ab3b0fcbe0a46f28c0b74db72333b51 +timeCreated: 1560807034 \ No newline at end of file diff --git a/Runtime/Choices/IChoice.cs b/Runtime/Choices/IChoice.cs new file mode 100644 index 0000000..deac2e4 --- /dev/null +++ b/Runtime/Choices/IChoice.cs @@ -0,0 +1,10 @@ +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Choices { + public interface IChoice : IUniqueId { + string Text { get; } + bool IsValid { get; } + + INode GetValidChildNode (); + } +} diff --git a/Runtime/Choices/IChoice.cs.meta b/Runtime/Choices/IChoice.cs.meta new file mode 100644 index 0000000..9a1636a --- /dev/null +++ b/Runtime/Choices/IChoice.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7a777c344eba4511963d5ef718daf2b1 +timeCreated: 1561063905 \ No newline at end of file diff --git a/Runtime/Conditions.meta b/Runtime/Conditions.meta new file mode 100644 index 0000000..aa2bd46 --- /dev/null +++ b/Runtime/Conditions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6855acc6e39449208d274a2c16449dc7 +timeCreated: 1560964854 \ No newline at end of file diff --git a/Runtime/Conditions/ConditionDataBase.cs b/Runtime/Conditions/ConditionDataBase.cs new file mode 100644 index 0000000..6b07b6c --- /dev/null +++ b/Runtime/Conditions/ConditionDataBase.cs @@ -0,0 +1,13 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Conditions { + public abstract class ConditionDataBase : NodeNestedDataBase, IConditionData { + public virtual void OnInit (IDialogueController dialogue) {} + public abstract bool OnGetIsValid (INode parent); + + public override ICondition GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new ConditionRuntime(dialogue, _uniqueId, Instantiate(this)); + } + } +} diff --git a/Runtime/Conditions/ConditionDataBase.cs.meta b/Runtime/Conditions/ConditionDataBase.cs.meta new file mode 100644 index 0000000..cf5b33c --- /dev/null +++ b/Runtime/Conditions/ConditionDataBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a04df09377384f649f09c6dd5f204709 +timeCreated: 1561068074 \ No newline at end of file diff --git a/Runtime/Conditions/ConditionRuntime.cs b/Runtime/Conditions/ConditionRuntime.cs new file mode 100644 index 0000000..dacf013 --- /dev/null +++ b/Runtime/Conditions/ConditionRuntime.cs @@ -0,0 +1,33 @@ +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Conditions { + public interface IConditionData { + void OnInit (IDialogueController dialogue); + bool OnGetIsValid (INode parent); + } + + public class ConditionRuntime : ICondition { + private readonly IDialogueController _dialogueController; + private readonly IConditionData _data; + + private bool _initTriggered; + + public string UniqueId { get; } + public IConditionData Data => _data; + + public ConditionRuntime (IDialogueController dialogueController, string uniqueId, IConditionData data) { + _data = data; + _dialogueController = dialogueController; + UniqueId = uniqueId; + } + + public bool GetIsValid (INode parent) { + if (!_initTriggered) { + _data.OnInit(_dialogueController); + _initTriggered = true; + } + + return _data.OnGetIsValid(parent); + } + } +} diff --git a/Runtime/Conditions/ConditionRuntime.cs.meta b/Runtime/Conditions/ConditionRuntime.cs.meta new file mode 100644 index 0000000..794d828 --- /dev/null +++ b/Runtime/Conditions/ConditionRuntime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2169ee23649040909badba5ba469f4d2 +timeCreated: 1561066972 \ No newline at end of file diff --git a/Runtime/Conditions/ICondition.cs b/Runtime/Conditions/ICondition.cs new file mode 100644 index 0000000..30034b8 --- /dev/null +++ b/Runtime/Conditions/ICondition.cs @@ -0,0 +1,9 @@ +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Conditions { + public interface ICondition : IUniqueId { + IConditionData Data { get; } + + bool GetIsValid (INode parent); + } +} diff --git a/Runtime/Conditions/ICondition.cs.meta b/Runtime/Conditions/ICondition.cs.meta new file mode 100644 index 0000000..162d665 --- /dev/null +++ b/Runtime/Conditions/ICondition.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54ca5577fd3d4768beebe893ac953cfe +timeCreated: 1560964872 \ No newline at end of file diff --git a/Runtime/DialogueController.meta b/Runtime/DialogueController.meta new file mode 100644 index 0000000..a721bf4 --- /dev/null +++ b/Runtime/DialogueController.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 496a3d005020407a887495eb6eaa6ac6 +timeCreated: 1585362066 \ No newline at end of file diff --git a/Runtime/DialogueController/DialogueController.cs b/Runtime/DialogueController/DialogueController.cs new file mode 100644 index 0000000..3e1bd22 --- /dev/null +++ b/Runtime/DialogueController/DialogueController.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using CleverCrow.Fluid.Dialogues.Nodes.PlayGraph; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + public interface IDialogueController { + IDatabaseInstance LocalDatabase { get; } + + void PlayChild (IGraphData graph); + } + + public class DialogueController : IDialogueController { + private readonly Stack _activeDialogue = new Stack(); + private readonly List _parentHierarchy = new(); + + public IDatabaseInstance LocalDatabase { get; } + public IDialogueEvents Events { get; } = new DialogueEvents(); + + /// + /// Playback runtime that corresponds to the current graph definition (may be nested) + /// + public IDialoguePlayback ActiveDialogue => _activeDialogue.Count > 0 ? _activeDialogue.Peek() : null; + + /// + /// Return the root of the currently playing dialogue + /// + public IGraphData RootGraph => _activeDialogue.Count > 0 ? _activeDialogue.ToArray().Last().Graph.Data : null; + + /// + /// Keeps track of the IDs of all nested graph node containers currently playing in order. Important for restoring nested + /// dialogue graphs that are currently playing from serialized data. + /// + public IReadOnlyList ParentHierarchy => _parentHierarchy; + + + public DialogueController (IDatabaseInstance localDatabase) { + LocalDatabase = localDatabase; + } + + public void Play (IDialoguePlayback playback) { + PlaybackSetup(playback); + playback.Play(); + } + + // @NOTE gameObjectOverrides will be deprecated. It can easily be replaced with a send message system that looks up the target GameObject by string. This is a lot to maintain and messy + public void Play (IGraphData graph) { + var runtime = new GraphRuntime(this, graph); + Play(new DialoguePlayback(runtime, this, new DialogueEvents())); + } + + // @TODO Needs some tests + public void Play (IGraphData graph, IReadOnlyList parentHierarchy, string nodeId) { + var runtime = new GraphRuntime(this, graph); + + // Setup the root graph with all proper hooks + var origin = new DialoguePlayback(runtime, this, new DialogueEvents()); + PlaybackSetup(origin); + + var playback = origin; + foreach (var id in parentHierarchy) { + // Set and get the pointer + playback.SetPointer(id); + if (playback.Pointer is not NodePlayGraph node) { + throw new InvalidOperationException($"Parent hierarchy ID {id} does not contain a nested graph"); + } + + // Add the child playback runtime + AddChild(node.Graph as DialogueGraph); + playback = _activeDialogue.Peek() as DialoguePlayback; + } + + playback.SetPointerAndPlay(nodeId); + } + + void PlaybackSetup (IDialoguePlayback playback) { + SetupDatabases(); + + Stop(); + + playback.Events.Speak.AddListener(TriggerSpeak); + playback.Events.SpeakWithAudio.AddListener(TriggerSpeakWithAudio); + playback.Events.Choice.AddListener(TriggerChoice); + playback.Events.NodeEnter.AddListener(TriggerEnterNode); + playback.Events.Begin.AddListener(TriggerBegin); + playback.Events.End.AddListener(TriggerEnd); + + _activeDialogue.Push(playback); + } + + private void SetupDatabases () { + LocalDatabase.Clear(); + } + + public void PlayChild (IDialoguePlayback playback) { + SetupChild(playback); + playback.Play(); + } + + public void PlayChild (IGraphData graph) { + // @TODO Test this + _parentHierarchy.Add(_activeDialogue.Peek().Pointer.UniqueId); + + var runtime = new GraphRuntime(this, graph); + PlayChild(new DialoguePlayback(runtime, this, new DialogueEvents())); + } + + void AddChild (IGraphData graph) { + _parentHierarchy.Add(_activeDialogue.Peek().Pointer.UniqueId); + + var runtime = new GraphRuntime(this, graph); + SetupChild(new DialoguePlayback(runtime, this, new DialogueEvents())); + } + + void SetupChild (IDialoguePlayback playback) { + if (ActiveDialogue == null) { + throw new InvalidOperationException("Cannot trigger child dialogue, nothing is playing"); + } + + var parentDialogue = ActiveDialogue; + playback.Events.End.AddListener(() => { + _activeDialogue.Pop(); + parentDialogue.Next(); + }); + playback.Events.Speak.AddListener(TriggerSpeak); + playback.Events.SpeakWithAudio.AddListener(TriggerSpeakWithAudio); + playback.Events.Choice.AddListener(TriggerChoice); + playback.Events.NodeEnter.AddListener(TriggerEnterNode); + + _activeDialogue.Push(playback); + } + + private void TriggerBegin () { + Events.Begin.Invoke(); + } + + private void TriggerEnd () { + _activeDialogue.Pop(); + Events.End.Invoke(); + } + + private void TriggerSpeakWithAudio (IActor actor, string text, AudioClip audioClip) { + Events.SpeakWithAudio.Invoke(actor, text, audioClip); + } + + private void TriggerSpeak (IActor actor, string text) { + Events.Speak.Invoke(actor, text); + } + + private void TriggerChoice (IActor actor, string text, List choices) { + Events.Choice.Invoke(actor, text, choices); + } + + private void TriggerEnterNode (INode node) { + Events.NodeEnter.Invoke(node); + } + + public void Next () { + ActiveDialogue?.Next(); + } + + public void Tick () { + ActiveDialogue?.Tick(); + } + + public void SelectChoice (int index) { + ActiveDialogue?.SelectChoice(index); + } + + public void Stop () { + foreach (var dialogue in _activeDialogue) { + dialogue.Stop(); + } + + _activeDialogue.Clear(); + // @TODO Test this + _parentHierarchy.Clear(); + } + + // @TODO Write a test for this if possible, might not be easy + /// + /// Verifies a nested graph can be played. Not the most runtime friendly operation so use sparingly + /// + public bool CanPlay (IGraphData graph, List parentHierarchy, string nodeId) { + // Use the parent hierarchy to find the nested graph + var nestedGraph = graph; + foreach (var id in parentHierarchy) { + var node = GetNode(nestedGraph, id); + if (node == null) return false; + + if (node is NodePlayGraphData playGraph) { + nestedGraph = playGraph.dialogueGraph; + } else { + return false; + } + } + + // Check if the node exists in the nested graph + return GetNode(nestedGraph, nodeId) != null; + } + + INodeData GetNode (IGraphData graph, string id) { + foreach (var node in graph.Nodes) { + if (node.UniqueId == id) { + return node; + } + } + + return null; + } + } +} diff --git a/Runtime/DialogueController/DialogueController.cs.meta b/Runtime/DialogueController/DialogueController.cs.meta new file mode 100644 index 0000000..edfc80b --- /dev/null +++ b/Runtime/DialogueController/DialogueController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2e4f8db06c7482c9765293663debefd +timeCreated: 1561159800 \ No newline at end of file diff --git a/Runtime/DialoguePlayback.cs b/Runtime/DialoguePlayback.cs new file mode 100644 index 0000000..9f43977 --- /dev/null +++ b/Runtime/DialoguePlayback.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + public interface IDialoguePlayback { + IDialogueEvents Events { get; } + IDialogueController ParentCtrl { get; } + + IGraph Graph { get; } + + /// + /// Current node data being used for the runtime + /// + INode Pointer { get; } + + void Next (); + void Play (); + void Tick (); + void SelectChoice (int index); + void Stop (); + } + + public class DialoguePlayback : IDialoguePlayback { + private bool _playing; + private readonly Queue _actionQueue = new Queue(); + private readonly IGraph _graph; + + public IDialogueEvents Events { get;} + public IDialogueController ParentCtrl { get; } + public INode Pointer { get; private set; } + public IGraph Graph => _graph; + + public DialoguePlayback (IGraph graph, IDialogueController ctrl, IDialogueEvents events) { + _graph = graph; + Events = events; + ParentCtrl = ctrl; + } + + public void Play () { + Stop(); + + _playing = true; + Pointer = _graph.Root; + Events.Begin.Invoke(); + + if (!_graph.Root.IsValid) { + Events.End.Invoke(); + return; + } + + Next(null, Pointer); + } + + private void ClearAllActions () { + while (_actionQueue.Count > 0) { + var action = _actionQueue.Dequeue(); + action.End(); + } + } + + private ActionStatus UpdateActionQueue () { + while (_actionQueue.Count > 0) { + if (_actionQueue.Peek().Tick() == ActionStatus.Continue) return ActionStatus.Continue; + _actionQueue.Dequeue(); + } + + return ActionStatus.Success; + } + + public void Next () { + if (_actionQueue.Count != 0) return; + var current = Pointer; + var next = Pointer.Next(); + Pointer = next; + + Next(current, next); + } + + private void Next (INode current, INode next) { + if (current != null) { + foreach (var action in current.ExitActions) { + _actionQueue.Enqueue(action); + } + } + + if (next != null) { + foreach (var action in next.EnterActions) { + _actionQueue.Enqueue(action); + } + } + + if (UpdateActionQueue() == ActionStatus.Continue) return; + UpdatePointer(next); + } + + private void UpdatePointer (INode pointer) { + if (pointer == null) { + Events.End.Invoke(); + _playing = false; + return; + } + + pointer.Play(this); + } + + public void Tick () { + if (_actionQueue.Count > 0 && UpdateActionQueue() == ActionStatus.Success) { + UpdatePointer(Pointer); + } + } + + public void Stop () { + Pointer = null; + ClearAllActions(); + + if (_playing) { + Events.End.Invoke(); + _playing = false; + } + } + + public void SelectChoice (int index) { + var choice = Pointer.GetChoice(index); + var current = Pointer; + Pointer = choice.GetValidChildNode(); + Next(current, Pointer); + } + + public void SetPointer (string id) { + _playing = true; + Pointer = _graph.GetNodeByDataId(id); + + if (Pointer == null) { + Debug.LogError($"Pointer not found when maually setting pointer: {id}. This graph will instantly end when run"); + } + } + + // Allows playing the pointer from a nested location without crashing + public void SetPointerAndPlay (string id) { + SetPointer(id); + Events.Begin.Invoke(); + + Next(null, Pointer); + } + } +} diff --git a/Runtime/DialoguePlayback.cs.meta b/Runtime/DialoguePlayback.cs.meta new file mode 100644 index 0000000..889da84 --- /dev/null +++ b/Runtime/DialoguePlayback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c36732fd241148b4a8a91bd95be0cbf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Graphs.meta b/Runtime/Graphs.meta new file mode 100644 index 0000000..8232ad8 --- /dev/null +++ b/Runtime/Graphs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b51410a60630450a97dbdefc46d16a38 +timeCreated: 1560534455 \ No newline at end of file diff --git a/Runtime/Graphs/DialogueGraph.cs b/Runtime/Graphs/DialogueGraph.cs new file mode 100644 index 0000000..2edf73e --- /dev/null +++ b/Runtime/Graphs/DialogueGraph.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Nodes; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Graphs { + public interface IGraphData { + INodeData Root { get; } + IReadOnlyList Nodes { get; } + } + + public class DialogueGraph : ScriptableObject, IGraphData { + [HideInInspector] + [SerializeField] + private List _nodes = new List(); + + [HideInInspector] + public NodeRootData root; + + [HideInInspector] + public Vector2 scrollPosition; + + public INodeData Root => root; + public IReadOnlyList Nodes => _nodes; + + public void AddNode (NodeDataBase node) { + node.Setup(); + _nodes.Add(node); + } + + public void DeleteNode (NodeDataBase node) { + _nodes.Remove(node); + } + } +} diff --git a/Runtime/Graphs/DialogueGraph.cs.meta b/Runtime/Graphs/DialogueGraph.cs.meta new file mode 100644 index 0000000..991a24e --- /dev/null +++ b/Runtime/Graphs/DialogueGraph.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b35851dfd8754a74b333944d50d05bbd +timeCreated: 1560881144 \ No newline at end of file diff --git a/Runtime/Graphs/Events.meta b/Runtime/Graphs/Events.meta new file mode 100644 index 0000000..112fca3 --- /dev/null +++ b/Runtime/Graphs/Events.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2883ad29ae14454288cdc24ec4ab1c88 +timeCreated: 1560535201 \ No newline at end of file diff --git a/Runtime/Graphs/Events/DialogueEvents.cs b/Runtime/Graphs/Events/DialogueEvents.cs new file mode 100644 index 0000000..eef135b --- /dev/null +++ b/Runtime/Graphs/Events/DialogueEvents.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using CleverCrow.Fluid.Utilities.UnityEvents; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + public class DialogueEvents : IDialogueEvents { + public IUnityEvent Begin { get; } = new UnityEventPlus(); + public IUnityEvent End { get; } = new UnityEventPlus(); + public IUnityEvent Speak { get; } = new UnityEventPlus(); + public IUnityEvent SpeakWithAudio { get; } = new UnityEventPlus(); + public IUnityEvent> Choice { get; } = new UnityEventPlus>(); + public IUnityEvent NodeEnter { get; } = new UnityEventPlus(); + } +} diff --git a/Runtime/Graphs/Events/DialogueEvents.cs.meta b/Runtime/Graphs/Events/DialogueEvents.cs.meta new file mode 100644 index 0000000..15ee411 --- /dev/null +++ b/Runtime/Graphs/Events/DialogueEvents.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 349b47944cfa4003ac13b8623a1b026e +timeCreated: 1561161196 \ No newline at end of file diff --git a/Runtime/Graphs/Events/IDialogueEvents.cs b/Runtime/Graphs/Events/IDialogueEvents.cs new file mode 100644 index 0000000..79a8996 --- /dev/null +++ b/Runtime/Graphs/Events/IDialogueEvents.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using CleverCrow.Fluid.Utilities.UnityEvents; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues { + public interface IDialogueEvents { + IUnityEvent Begin { get; } + IUnityEvent End { get; } + + /// + /// Old Speak event, use SpeakWithAudio to get audio sound files + /// + IUnityEvent Speak { get; } + IUnityEvent SpeakWithAudio { get; } + + IUnityEvent> Choice { get; } + IUnityEvent NodeEnter { get; } + } +} diff --git a/Runtime/Graphs/Events/IDialogueEvents.cs.meta b/Runtime/Graphs/Events/IDialogueEvents.cs.meta new file mode 100644 index 0000000..e5eb2d1 --- /dev/null +++ b/Runtime/Graphs/Events/IDialogueEvents.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 01b0202fb9fc434ba792ddfb9ec97b28 +timeCreated: 1560535300 \ No newline at end of file diff --git a/Runtime/Graphs/GraphRuntime.cs b/Runtime/Graphs/GraphRuntime.cs new file mode 100644 index 0000000..7d09aee --- /dev/null +++ b/Runtime/Graphs/GraphRuntime.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Graphs { + public class GraphRuntime : IGraph { + private readonly Dictionary _dataToRuntime; + + public INode Root { get; } + public IGraphData Data { get; } + + public GraphRuntime (IDialogueController dialogue, IGraphData data) { + _dataToRuntime = data.Nodes.ToDictionary( + k => k, + v => v.GetRuntime(this, dialogue)); + + Root = GetCopy(data.Root); + Data = data; + } + + public INode GetCopy (INodeData original) { + return _dataToRuntime[original]; + } + + public INode GetNodeByDataId (string id) { + return _dataToRuntime.FirstOrDefault(n => n.Key.UniqueId == id).Value; + } + } +} diff --git a/Runtime/Graphs/GraphRuntime.cs.meta b/Runtime/Graphs/GraphRuntime.cs.meta new file mode 100644 index 0000000..9e3d216 --- /dev/null +++ b/Runtime/Graphs/GraphRuntime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ed1ddfb43db6408583940ff344fe37a1 +timeCreated: 1560874957 \ No newline at end of file diff --git a/Runtime/Graphs/IGraph.cs b/Runtime/Graphs/IGraph.cs new file mode 100644 index 0000000..e3ac4d9 --- /dev/null +++ b/Runtime/Graphs/IGraph.cs @@ -0,0 +1,11 @@ +using CleverCrow.Fluid.Dialogues.Nodes; + +namespace CleverCrow.Fluid.Dialogues.Graphs { + public interface IGraph { + INode Root { get; } + IGraphData Data { get; } + + INode GetCopy (INodeData nodeData); + INode GetNodeByDataId (string id); + } +} diff --git a/Runtime/Graphs/IGraph.cs.meta b/Runtime/Graphs/IGraph.cs.meta new file mode 100644 index 0000000..ca119ee --- /dev/null +++ b/Runtime/Graphs/IGraph.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f27a8e2703e346ceac04293e764dfd40 +timeCreated: 1561064260 \ No newline at end of file diff --git a/Runtime/IGetRuntime.cs b/Runtime/IGetRuntime.cs new file mode 100644 index 0000000..7c37ec1 --- /dev/null +++ b/Runtime/IGetRuntime.cs @@ -0,0 +1,7 @@ +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues { + public interface IGetRuntime : ISetup { + T GetRuntime (IGraph graphRuntime, IDialogueController dialogue); + } +} diff --git a/Runtime/IGetRuntime.cs.meta b/Runtime/IGetRuntime.cs.meta new file mode 100644 index 0000000..f2d3c64 --- /dev/null +++ b/Runtime/IGetRuntime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a42a6ce31a8044b9909e03ff3799b35a +timeCreated: 1561065494 \ No newline at end of file diff --git a/Runtime/ISetup.cs b/Runtime/ISetup.cs new file mode 100644 index 0000000..a98d592 --- /dev/null +++ b/Runtime/ISetup.cs @@ -0,0 +1,5 @@ +namespace CleverCrow.Fluid.Dialogues { + public interface ISetup : IUniqueId { + void Setup (); + } +} diff --git a/Runtime/ISetup.cs.meta b/Runtime/ISetup.cs.meta new file mode 100644 index 0000000..e98e24c --- /dev/null +++ b/Runtime/ISetup.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 733c970e901945ceb7bb8ffc2f986755 +timeCreated: 1561395082 \ No newline at end of file diff --git a/Runtime/IUniqueId.cs b/Runtime/IUniqueId.cs new file mode 100644 index 0000000..ef216da --- /dev/null +++ b/Runtime/IUniqueId.cs @@ -0,0 +1,5 @@ +namespace CleverCrow.Fluid.Dialogues { + public interface IUniqueId { + string UniqueId { get; } + } +} diff --git a/Runtime/IUniqueId.cs.meta b/Runtime/IUniqueId.cs.meta new file mode 100644 index 0000000..2e8f7f4 --- /dev/null +++ b/Runtime/IUniqueId.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3efe90ba4e3a4bdbace083862475f87e +timeCreated: 1561395642 \ No newline at end of file diff --git a/Runtime/Nodes.meta b/Runtime/Nodes.meta new file mode 100644 index 0000000..fd6b723 --- /dev/null +++ b/Runtime/Nodes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d3b3c102a3944eaf8639a1b0e88f51a2 +timeCreated: 1560804784 \ No newline at end of file diff --git a/Runtime/Nodes/ChoiceHub.meta b/Runtime/Nodes/ChoiceHub.meta new file mode 100644 index 0000000..9fc865f --- /dev/null +++ b/Runtime/Nodes/ChoiceHub.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 807c809a304b4df987e66b261a349fdf +timeCreated: 1561143174 \ No newline at end of file diff --git a/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs b/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs new file mode 100644 index 0000000..ddd1d69 --- /dev/null +++ b/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeChoiceHub : INode { + private readonly List _choiceList; + private List _conditions; + + public string UniqueId { get; } + public List EnterActions { get; } + public List ExitActions { get; } + public IReadOnlyList Conditions => _conditions; + + public virtual bool IsValid => + _conditions.Find(c => !c.GetIsValid(this)) == null; + + public List HubChoices => + _choiceList.Where(c => c.IsValid).ToList(); + + public NodeChoiceHub (string uniqueId, List choiceList, List conditions) { + UniqueId = uniqueId; + _choiceList = choiceList; + _conditions = conditions; + } + + public INode Next () { + throw new System.NotImplementedException(); + } + + public void Play (IDialoguePlayback playback) { + throw new System.NotImplementedException(); + } + + public IChoice GetChoice (int index) { + throw new System.NotImplementedException(); + } + } +} diff --git a/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs.meta b/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs.meta new file mode 100644 index 0000000..582fd22 --- /dev/null +++ b/Runtime/Nodes/ChoiceHub/NodeChoiceHub.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0d7c638af79e40cbac660ad8821c8c1f +timeCreated: 1561143186 \ No newline at end of file diff --git a/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs b/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs new file mode 100644 index 0000000..f34abf8 --- /dev/null +++ b/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs @@ -0,0 +1,18 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + [CreateMenu("Hub/Choice")] + public class NodeChoiceHubData : NodeDataChoiceBase { + protected override string DefaultName => "Choice Hub"; + public override bool HideInspectorActions => true; + + public override INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + var runtimeChoices = choices.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(); + return new NodeChoiceHub( + UniqueId, + runtimeChoices, + conditions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList()); + } + } +} diff --git a/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs.meta b/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs.meta new file mode 100644 index 0000000..2180f79 --- /dev/null +++ b/Runtime/Nodes/ChoiceHub/NodeChoiceHubData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9ee683620015454489ed039e9cb805da +timeCreated: 1561258615 \ No newline at end of file diff --git a/Runtime/Nodes/Dialogue.meta b/Runtime/Nodes/Dialogue.meta new file mode 100644 index 0000000..a3eded8 --- /dev/null +++ b/Runtime/Nodes/Dialogue.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b6f9ec99337b48b4af08d6cb84d69479 +timeCreated: 1561064359 \ No newline at end of file diff --git a/Runtime/Nodes/Dialogue/NodeDialogue.cs b/Runtime/Nodes/Dialogue/NodeDialogue.cs new file mode 100644 index 0000000..cffe57f --- /dev/null +++ b/Runtime/Nodes/Dialogue/NodeDialogue.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeDialogue : NodeBase { + private readonly IActor _actor; + private readonly string _dialogue; + private readonly List _choices; + + private List _emittedChoices; + private readonly AudioClip _audioClip; + + public NodeDialogue ( + IGraph graph, + string uniqueId, + IActor actor, + string dialogue, + AudioClip audioClip, + List children, + List choices, + List conditions, + List enterActions, + List exitActions) : + base(graph, uniqueId, children, conditions, enterActions, exitActions) { + _actor = actor; + _dialogue = dialogue; + _choices = choices; + _audioClip = audioClip; + } + + private List GetValidChoices (IDialoguePlayback playback) { + var child = Next(); + if (_choices.Count == 0 && child?.HubChoices != null && child.HubChoices.Count > 0) { + playback.Events.NodeEnter.Invoke(child); + return child.HubChoices; + } + + return _choices.Where(c => c.IsValid).ToList(); + } + + protected override void OnPlay (IDialoguePlayback playback) { + _emittedChoices = GetValidChoices(playback); + if (_emittedChoices.Count > 0) { + playback.Events.Choice.Invoke(_actor, _dialogue, _emittedChoices); + return; + } + + playback.Events.Speak.Invoke(_actor, _dialogue); + playback.Events.SpeakWithAudio.Invoke(_actor, _dialogue, _audioClip); + } + + public override IChoice GetChoice (int index) { + if (index >= _emittedChoices.Count) return null; + + return _emittedChoices[index]; + } + } +} diff --git a/Runtime/Nodes/Dialogue/NodeDialogue.cs.meta b/Runtime/Nodes/Dialogue/NodeDialogue.cs.meta new file mode 100644 index 0000000..077b1fc --- /dev/null +++ b/Runtime/Nodes/Dialogue/NodeDialogue.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ba494c8ab3ba4950b8e6e8c2a6dc7183 +timeCreated: 1560805092 \ No newline at end of file diff --git a/Runtime/Nodes/Dialogue/NodeDialogueData.cs b/Runtime/Nodes/Dialogue/NodeDialogueData.cs new file mode 100644 index 0000000..9354422 --- /dev/null +++ b/Runtime/Nodes/Dialogue/NodeDialogueData.cs @@ -0,0 +1,32 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + [CreateMenu("Dialogue", 1)] + public class NodeDialogueData : NodeDataChoiceBase { + public ActorDefinition actor; + public AudioClip audio; + + [TextArea] + public string dialogue; + + protected override string DefaultName => "Dialogue"; + public override string Text => dialogue; + + public override INode GetRuntime (IGraph graphRuntime, IDialogueController controller) { + return new NodeDialogue( + graphRuntime, + UniqueId, + actor, + dialogue, + audio, + children.ToList(), + choices.Select(c => c.GetRuntime(graphRuntime, controller)).ToList(), + conditions.Select(c => c.GetRuntime(graphRuntime, controller)).ToList(), + enterActions.Select(a => a.GetRuntime(graphRuntime, controller)).ToList(), + exitActions.Select(a => a.GetRuntime(graphRuntime, controller)).ToList() + ); + } + } +} diff --git a/Runtime/Nodes/Dialogue/NodeDialogueData.cs.meta b/Runtime/Nodes/Dialogue/NodeDialogueData.cs.meta new file mode 100644 index 0000000..288b65c --- /dev/null +++ b/Runtime/Nodes/Dialogue/NodeDialogueData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9f157564dbc846468ec0ac2b6f7d545e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - actor: {fileID: 11400000, guid: aebb863e25cae0a4495c09b6a617ecf6, type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Nodes/Hub.meta b/Runtime/Nodes/Hub.meta new file mode 100644 index 0000000..6a99837 --- /dev/null +++ b/Runtime/Nodes/Hub.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 885ddf2a5c6b44878d8c80859c042ffa +timeCreated: 1561139669 \ No newline at end of file diff --git a/Runtime/Nodes/Hub/NodeHub.cs b/Runtime/Nodes/Hub/NodeHub.cs new file mode 100644 index 0000000..84092c1 --- /dev/null +++ b/Runtime/Nodes/Hub/NodeHub.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeHub : NodeBase { + public NodeHub ( + IGraph graph, + string uniqueId, + List children, + List conditions, + List enterActions, + List exitActions) : + base(graph, uniqueId, children, conditions, enterActions, exitActions) { + } + } +} diff --git a/Runtime/Nodes/Hub/NodeHub.cs.meta b/Runtime/Nodes/Hub/NodeHub.cs.meta new file mode 100644 index 0000000..cc52855 --- /dev/null +++ b/Runtime/Nodes/Hub/NodeHub.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: de6b5fe462b8431c92526ef417895a94 +timeCreated: 1561139688 \ No newline at end of file diff --git a/Runtime/Nodes/Hub/NodeHubData.cs b/Runtime/Nodes/Hub/NodeHubData.cs new file mode 100644 index 0000000..dbf5055 --- /dev/null +++ b/Runtime/Nodes/Hub/NodeHubData.cs @@ -0,0 +1,19 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + [CreateMenu("Hub/Default")] + public class NodeHubData : NodeDataBase { + protected override string DefaultName => "Hub"; + public override INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new NodeHub( + graphRuntime, + UniqueId, + children.ToList(), + conditions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + enterActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + exitActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList() + ); + } + } +} diff --git a/Runtime/Nodes/Hub/NodeHubData.cs.meta b/Runtime/Nodes/Hub/NodeHubData.cs.meta new file mode 100644 index 0000000..862bbb1 --- /dev/null +++ b/Runtime/Nodes/Hub/NodeHubData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a5bb286b3994795a7780dbfc28d37fd +timeCreated: 1561258453 \ No newline at end of file diff --git a/Runtime/Nodes/INode.cs b/Runtime/Nodes/INode.cs new file mode 100644 index 0000000..a0af434 --- /dev/null +++ b/Runtime/Nodes/INode.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public interface INode : IUniqueId { + List EnterActions { get; } + List ExitActions { get; } + bool IsValid { get; } + List HubChoices { get; } + IReadOnlyList Conditions { get; } + + /// + /// Returns the first valid child node + /// + INode Next (); + void Play (IDialoguePlayback playback); + IChoice GetChoice (int index); + } +} diff --git a/Runtime/Nodes/INode.cs.meta b/Runtime/Nodes/INode.cs.meta new file mode 100644 index 0000000..e7e68d1 --- /dev/null +++ b/Runtime/Nodes/INode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2a992e4f5d94c83a9964ab37c38c95c +timeCreated: 1560534567 \ No newline at end of file diff --git a/Runtime/Nodes/Links.meta b/Runtime/Nodes/Links.meta new file mode 100644 index 0000000..2b9d29d --- /dev/null +++ b/Runtime/Nodes/Links.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a819704fd5d34c88a782f57f3c7557e1 +timeCreated: 1561135140 \ No newline at end of file diff --git a/Runtime/Nodes/Links/NodeLink.cs b/Runtime/Nodes/Links/NodeLink.cs new file mode 100644 index 0000000..86cb484 --- /dev/null +++ b/Runtime/Nodes/Links/NodeLink.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeLink : NodeBase { + public override bool IsValid => Children[0]?.IsValid ?? false; + + public NodeLink ( + IGraph graph, + string UniqueId, + INodeData child, + List conditions, + List enterActions, + List exitActions) : + base(graph, UniqueId, new List{child}, conditions, enterActions, exitActions) { + } + } +} diff --git a/Runtime/Nodes/Links/NodeLink.cs.meta b/Runtime/Nodes/Links/NodeLink.cs.meta new file mode 100644 index 0000000..bd6dfa2 --- /dev/null +++ b/Runtime/Nodes/Links/NodeLink.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7939d551d49b4c608f8a4a579d51235d +timeCreated: 1561135150 \ No newline at end of file diff --git a/Runtime/Nodes/Links/NodeLinkData.cs b/Runtime/Nodes/Links/NodeLinkData.cs new file mode 100644 index 0000000..987358e --- /dev/null +++ b/Runtime/Nodes/Links/NodeLinkData.cs @@ -0,0 +1,17 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeLinkData : NodeDataBase { + public override INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new NodeLink( + graphRuntime, + UniqueId, + children.Count > 0 ? children[0] : null, + conditions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + enterActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + exitActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList() + ); + } + } +} diff --git a/Runtime/Nodes/Links/NodeLinkData.cs.meta b/Runtime/Nodes/Links/NodeLinkData.cs.meta new file mode 100644 index 0000000..eaa7c76 --- /dev/null +++ b/Runtime/Nodes/Links/NodeLinkData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d766ad0e6f2f48cbaa50eb4a799b4ec8 +timeCreated: 1561258275 \ No newline at end of file diff --git a/Runtime/Nodes/NodeBase.cs b/Runtime/Nodes/NodeBase.cs new file mode 100644 index 0000000..d4a5434 --- /dev/null +++ b/Runtime/Nodes/NodeBase.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public abstract class NodeBase : INode { + private readonly List _children; + private readonly List _conditions; + private readonly IGraph _runtime; + private List _childrenRuntimeCache; + + public List EnterActions { get; } + public List ExitActions { get; } + public virtual bool IsValid => + _conditions.Find(c => !c.GetIsValid(this)) == null; + public List HubChoices { get; } + public string UniqueId { get; } + public IReadOnlyList Conditions => _conditions; + + protected List Children => + _childrenRuntimeCache ?? + (_childrenRuntimeCache = _children.Select(_runtime.GetCopy).ToList()); + + protected NodeBase ( + IGraph runtime, + string uniqueId, + List children, + List conditions, + List enterActions, + List exitActions + ) { + _runtime = runtime; + UniqueId = uniqueId; + _children = children; + _conditions = conditions; + EnterActions = enterActions; + ExitActions = exitActions; + } + + public INode Next () { + return Children.Find(n => n.IsValid); + } + + public void Play (IDialoguePlayback playback) { + playback.Events.NodeEnter.Invoke(this); + OnPlay(playback); + } + + protected virtual void OnPlay (IDialoguePlayback playback) { + playback.Next(); + } + + public virtual IChoice GetChoice (int index) { + throw new System.NotImplementedException(); + } + } +} diff --git a/Runtime/Nodes/NodeBase.cs.meta b/Runtime/Nodes/NodeBase.cs.meta new file mode 100644 index 0000000..a9bdf89 --- /dev/null +++ b/Runtime/Nodes/NodeBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bb0856c7842343ee9d29fa0a71e39739 +timeCreated: 1561248711 \ No newline at end of file diff --git a/Runtime/Nodes/NodeDataBase.cs b/Runtime/Nodes/NodeDataBase.cs new file mode 100644 index 0000000..7e951eb --- /dev/null +++ b/Runtime/Nodes/NodeDataBase.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public interface INodeData : IGetRuntime, IConnectionChildCollection { + string Text { get; } + List Choices { get; } + } + + public interface IConnectionChildCollection { + IReadOnlyList Children { get; } + + void AddConnectionChild (NodeDataBase child); + void RemoveConnectionChild (NodeDataBase child); + void SortConnectionsByPosition (); + void ClearConnectionChildren (); + } + + public abstract class NodeDataBase : ScriptableObject, INodeData { + [HideInInspector] + [SerializeField] + private string _uniqueId; + + [HideInInspector] + public Rect rect; + + public string nodeTitle; + + [HideInInspector] + public List children = new List(); + + [HideInInspector] + public List conditions = new List(); + + [HideInInspector] + public List enterActions = new List(); + + [HideInInspector] + public List exitActions = new List(); + + public string UniqueId => _uniqueId; + protected virtual string DefaultName { get; } = "Untitled"; + public IReadOnlyList Children => children; + public virtual bool HideConnections => false; + public virtual bool HideInspectorActions => false; + public virtual bool HideInspectorConditions => false; + public virtual string Text => ""; + public virtual List Choices { get; } = new List(); + + public void Setup () { + _uniqueId = Guid.NewGuid().ToString(); + name = DefaultName; + } + + public void AddConnectionChild (NodeDataBase child) { + children.Add(child); + } + + public void RemoveConnectionChild (NodeDataBase child) { + children.Remove(child); + } + + public virtual void SortConnectionsByPosition () { + children = children.OrderBy(i => i.rect.yMin).ToList(); + } + + public abstract INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue); + + public virtual void ClearConnectionChildren () { + children.Clear(); + } + + public virtual NodeDataBase GetDataCopy () { + var copy = Instantiate(this); + copy.conditions = conditions.Select(Instantiate).ToList(); + copy.enterActions = enterActions.Select(Instantiate).ToList(); + copy.exitActions = exitActions.Select(Instantiate).ToList(); + + return copy; + } + } +} diff --git a/Runtime/Nodes/NodeDataBase.cs.meta b/Runtime/Nodes/NodeDataBase.cs.meta new file mode 100644 index 0000000..092d600 --- /dev/null +++ b/Runtime/Nodes/NodeDataBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 769ed8c876434b2bb94f56d2b6f00e3c +timeCreated: 1560889351 \ No newline at end of file diff --git a/Runtime/Nodes/NodeDataChoiceBase.cs b/Runtime/Nodes/NodeDataChoiceBase.cs new file mode 100644 index 0000000..0c1595f --- /dev/null +++ b/Runtime/Nodes/NodeDataChoiceBase.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using CleverCrow.Fluid.Dialogues.Choices; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public abstract class NodeDataChoiceBase : NodeDataBase { + [HideInInspector] + public List choices = new List(); + + public override List Choices => choices; + + public override void ClearConnectionChildren () { + base.ClearConnectionChildren(); + foreach (var choice in choices) { + choice.ClearConnectionChildren(); + } + } + + public override void SortConnectionsByPosition () { + base.SortConnectionsByPosition(); + foreach (var choice in choices) { + choice.SortConnectionsByPosition(); + } + } + + public override NodeDataBase GetDataCopy () { + var copy = base.GetDataCopy() as NodeDataChoiceBase; + copy.choices = choices.Select(Instantiate).ToList(); + + return copy; + } + } +} diff --git a/Runtime/Nodes/NodeDataChoiceBase.cs.meta b/Runtime/Nodes/NodeDataChoiceBase.cs.meta new file mode 100644 index 0000000..d0f5eae --- /dev/null +++ b/Runtime/Nodes/NodeDataChoiceBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1c703fee9e214b539f1087da592d7ac7 +timeCreated: 1564348864 \ No newline at end of file diff --git a/Runtime/Nodes/NodeNestedDataBase.cs b/Runtime/Nodes/NodeNestedDataBase.cs new file mode 100644 index 0000000..4b729b8 --- /dev/null +++ b/Runtime/Nodes/NodeNestedDataBase.cs @@ -0,0 +1,26 @@ +using System; +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public abstract class NodeNestedDataBase : ScriptableObject, IGetRuntime { + [SerializeField] + private string _title; + + [SerializeField] + protected string _uniqueId; + + public string UniqueId => _uniqueId; + + public void Setup () { + if (string.IsNullOrEmpty(_title)) { + _title = GetType().Name; + } + + name = GetType().Name; + _uniqueId = Guid.NewGuid().ToString(); + } + + public abstract T GetRuntime (IGraph graphRuntime, IDialogueController dialogue); + } +} diff --git a/Runtime/Nodes/NodeNestedDataBase.cs.meta b/Runtime/Nodes/NodeNestedDataBase.cs.meta new file mode 100644 index 0000000..432c017 --- /dev/null +++ b/Runtime/Nodes/NodeNestedDataBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8fd5ee15c0fa444791f7610fa98df38c +timeCreated: 1564947544 \ No newline at end of file diff --git a/Runtime/Nodes/Note.meta b/Runtime/Nodes/Note.meta new file mode 100644 index 0000000..7900a06 --- /dev/null +++ b/Runtime/Nodes/Note.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8f9d607bf1aa4cdaa79a340358db94ea +timeCreated: 1713803227 \ No newline at end of file diff --git a/Runtime/Nodes/Note/NodeNoteData.cs b/Runtime/Nodes/Note/NodeNoteData.cs new file mode 100644 index 0000000..3b86ccc --- /dev/null +++ b/Runtime/Nodes/Note/NodeNoteData.cs @@ -0,0 +1,25 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using UnityEngine; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + // @TODO This should inherit a simpler NodeDataBase that doesn't have children, conditions, enterActions, exitActions, ect. + // This can be done by breaking down the base NodeDataBase into a separate NodeDataHierarchyBase class that inherits from a minimal NodeDataBase + // The editor display will also need to be adjusted accordingly + [CreateMenu("Note")] + public class NodeNoteData : NodeDataBase { + [TextArea] + public string note; + + protected override string DefaultName => "Note"; + public override string Text => note; + + public override bool HideConnections => true; + public override bool HideInspectorActions => true; + public override bool HideInspectorConditions => true; + + public override INode GetRuntime (IGraph graphRuntime, IDialogueController controller) { + // There is no runtime, this is an editor only note + return null; + } + } +} diff --git a/Runtime/Nodes/Note/NodeNoteData.cs.meta b/Runtime/Nodes/Note/NodeNoteData.cs.meta new file mode 100644 index 0000000..d3b9fca --- /dev/null +++ b/Runtime/Nodes/Note/NodeNoteData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9bde9a25e35e47d5bc8e840897ea28e6 +timeCreated: 1713803266 \ No newline at end of file diff --git a/Runtime/Nodes/PlayGraph.meta b/Runtime/Nodes/PlayGraph.meta new file mode 100644 index 0000000..9fe9fd5 --- /dev/null +++ b/Runtime/Nodes/PlayGraph.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1f09af5ebfb14b94ad0938bd4ec11a62 +timeCreated: 1561152988 \ No newline at end of file diff --git a/Runtime/Nodes/PlayGraph/NodePlayGraph.cs b/Runtime/Nodes/PlayGraph/NodePlayGraph.cs new file mode 100644 index 0000000..5ff82af --- /dev/null +++ b/Runtime/Nodes/PlayGraph/NodePlayGraph.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes.PlayGraph { + public class NodePlayGraph : NodeBase { + private readonly IGraphData _graph; + + public IGraphData Graph => _graph; + + public NodePlayGraph ( + IGraph runtime, + string uniqueId, + IGraphData graph, + List children, + List conditions, + List enterActions, + List exitActions) + : base(runtime, uniqueId, children, conditions, enterActions, exitActions) { + _graph = graph; + } + + protected override void OnPlay (IDialoguePlayback playback) { + playback.ParentCtrl.PlayChild(_graph); + } + } +} diff --git a/Runtime/Nodes/PlayGraph/NodePlayGraph.cs.meta b/Runtime/Nodes/PlayGraph/NodePlayGraph.cs.meta new file mode 100644 index 0000000..9b31a24 --- /dev/null +++ b/Runtime/Nodes/PlayGraph/NodePlayGraph.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ef82cdad9ac4874a4a45881ae599aa5 +timeCreated: 1561153303 \ No newline at end of file diff --git a/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs b/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs new file mode 100644 index 0000000..51da9a6 --- /dev/null +++ b/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs @@ -0,0 +1,24 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes.PlayGraph; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + [CreateMenu("Play Graph")] + public class NodePlayGraphData : NodeDataBase { + public DialogueGraph dialogueGraph; + + protected override string DefaultName => "Play Graph"; + + public override INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new NodePlayGraph( + graphRuntime, + UniqueId, + dialogueGraph, + children.ToList(), + conditions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + enterActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + exitActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList() + ); + } + } +} diff --git a/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs.meta b/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs.meta new file mode 100644 index 0000000..eedcc5e --- /dev/null +++ b/Runtime/Nodes/PlayGraph/NodePlayGraphData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e67818104cf34e2693b49316191c2be0 +timeCreated: 1561258043 \ No newline at end of file diff --git a/Runtime/Nodes/Root.meta b/Runtime/Nodes/Root.meta new file mode 100644 index 0000000..f1093b3 --- /dev/null +++ b/Runtime/Nodes/Root.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 041a6738f3974a9fb81febaf66ebfc16 +timeCreated: 1561069920 \ No newline at end of file diff --git a/Runtime/Nodes/Root/NodeRoot.cs b/Runtime/Nodes/Root/NodeRoot.cs new file mode 100644 index 0000000..964fa31 --- /dev/null +++ b/Runtime/Nodes/Root/NodeRoot.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Conditions; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeRoot : NodeBase { + public NodeRoot ( + IGraph runtime, + string uniqueId, + List children, + List conditions, + List enterActions, + List exitActions) : + base(runtime, uniqueId, children, conditions, enterActions, exitActions) { + } + } +} diff --git a/Runtime/Nodes/Root/NodeRoot.cs.meta b/Runtime/Nodes/Root/NodeRoot.cs.meta new file mode 100644 index 0000000..5d4a17b --- /dev/null +++ b/Runtime/Nodes/Root/NodeRoot.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0360d9bb2c7e445897fc5b204c0ca973 +timeCreated: 1561069936 \ No newline at end of file diff --git a/Runtime/Nodes/Root/NodeRootData.cs b/Runtime/Nodes/Root/NodeRootData.cs new file mode 100644 index 0000000..b758fd9 --- /dev/null +++ b/Runtime/Nodes/Root/NodeRootData.cs @@ -0,0 +1,19 @@ +using System.Linq; +using CleverCrow.Fluid.Dialogues.Graphs; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeRootData : NodeDataBase { + protected override string DefaultName => "Root"; + + public override INode GetRuntime (IGraph graphRuntime, IDialogueController dialogue) { + return new NodeRoot( + graphRuntime, + UniqueId, + children.ToList(), + conditions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + enterActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList(), + exitActions.Select(c => c.GetRuntime(graphRuntime, dialogue)).ToList() + ); + } + } +} diff --git a/Runtime/Nodes/Root/NodeRootData.cs.meta b/Runtime/Nodes/Root/NodeRootData.cs.meta new file mode 100644 index 0000000..74b9e17 --- /dev/null +++ b/Runtime/Nodes/Root/NodeRootData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2d8d0566afe4d5fa9596c80df966df6 +timeCreated: 1561071172 \ No newline at end of file diff --git a/Runtime/com.fluid.dialogue.asmdef b/Runtime/com.fluid.dialogue.asmdef new file mode 100644 index 0000000..92d317f --- /dev/null +++ b/Runtime/com.fluid.dialogue.asmdef @@ -0,0 +1,17 @@ +{ + "name": "com.fluid.dialogue", + "references": [ + "com.fluid.database", + "com.fluid.unity-event-plus", + "com.fluid.simple-singleton" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/com.fluid.dialogue.asmdef.meta b/Runtime/com.fluid.dialogue.asmdef.meta new file mode 100644 index 0000000..f293b62 --- /dev/null +++ b/Runtime/com.fluid.dialogue.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b7c9b06e8d6046f41a510e4c2bf60be0 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests.meta b/Tests.meta new file mode 100644 index 0000000..e04fc00 --- /dev/null +++ b/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b02ce844932fd3d43bcb98acc3ff5ded +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor.meta b/Tests/Editor.meta new file mode 100644 index 0000000..74a7080 --- /dev/null +++ b/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 72cec465862da10488812ac6737a6d44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Actions.meta b/Tests/Editor/Actions.meta new file mode 100644 index 0000000..a34d4c4 --- /dev/null +++ b/Tests/Editor/Actions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cfc8b05cf46442a0838cfcc34286e893 +timeCreated: 1561928216 \ No newline at end of file diff --git a/Tests/Editor/Actions/ActionRuntimeTest.cs b/Tests/Editor/Actions/ActionRuntimeTest.cs new file mode 100644 index 0000000..f812d20 --- /dev/null +++ b/Tests/Editor/Actions/ActionRuntimeTest.cs @@ -0,0 +1,154 @@ +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions { + public class ActionRuntimeTest { + private IActionData _data; + private IDialogueController _dialogue; + private ActionRuntime _action; + + [SetUp] + public void BeforeEach () { + _data = Substitute.For(); + _data.OnUpdate().Returns(ActionStatus.Continue); + + _dialogue = Substitute.For(); + _action = new ActionRuntime(_dialogue, null, _data); + } + + public class TickMethod { + public class OnInitTriggering : ActionRuntimeTest { + [Test] + public void It_should_trigger_OnInit_with_a_dialogue_controller () { + _action.Tick(); + + _data.Received(1).OnInit(_dialogue); + } + + [Test] + public void It_should_trigger_OnInit_only_once () { + _action.Tick(); + _action.Tick(); + + _data.Received(1).OnInit(_dialogue); + } + } + + public class OnStartTriggering : ActionRuntimeTest { + [Test] + public void It_should_trigger_OnStart () { + _action.Tick(); + + _data.Received(1).OnStart(); + } + + [Test] + public void It_should_not_trigger_OnStart_again () { + _action.Tick(); + _action.Tick(); + + _data.Received(1).OnStart(); + } + + [Test] + public void It_should_trigger_OnStart_after_OnUpdate_returns_success () { + _action.Tick(); + _data.OnUpdate().Returns(ActionStatus.Success); + _action.Tick(); + _action.Tick(); + + _data.Received(2).OnStart(); + } + } + + public class OnUpdateTriggering : ActionRuntimeTest { + [Test] + public void It_should_trigger_OnUpdate () { + _action.Tick(); + + _data.Received(1).OnUpdate(); + } + + [Test] + public void It_should_return_the_update_status () { + _data.OnUpdate().Returns(ActionStatus.Continue); + + var status = _action.Tick(); + + Assert.AreEqual(ActionStatus.Continue, status); + } + } + + public class OnExitTriggering : ActionRuntimeTest { + [Test] + public void It_should_trigger_OnExit_if_OnUpdate_returns_success () { + _data.OnUpdate().Returns(ActionStatus.Success); + + _action.Tick(); + + _data.Received(1).OnExit(); + } + + [Test] + public void It_should_not_trigger_OnExit_if_OnUpdate_returns_continue () { + _data.OnUpdate().Returns(ActionStatus.Continue); + + _action.Tick(); + + _data.Received(0).OnExit(); + } + } + + public class OnResetTriggering : ActionRuntimeTest { + [Test] + public void It_should_trigger_reset_after_OnUpdate_returns_success () { + _data.OnUpdate().Returns(ActionStatus.Success); + + _action.Tick(); + _action.Tick(); + + _data.Received(1).OnReset(); + } + + [Test] + public void It_should_not_trigger_reset_after_OnUpdate_returns_continue () { + _data.OnUpdate().Returns(ActionStatus.Continue); + + _action.Tick(); + _action.Tick(); + + _data.Received(0).OnReset(); + } + } + + public class EndMethod : ActionRuntimeTest { + [Test] + public void It_should_not_call_OnExit_if_Tick_returns_success () { + _data.OnUpdate().Returns(ActionStatus.Success); + + _action.Tick(); + _action.End(); + + _data.Received(1).OnExit(); + } + + [Test] + public void It_should_call_OnExit_if_Tick_returned_continue () { + _data.OnUpdate().Returns(ActionStatus.Continue); + + _action.Tick(); + _action.End(); + + _data.Received(1).OnExit(); + } + + [Test] + public void It_should_not_call_OnExit_if_Tick_has_not_been_called () { + _action.End(); + + _data.Received(0).OnExit(); + } + } + } + } +} diff --git a/Tests/Editor/Actions/ActionRuntimeTest.cs.meta b/Tests/Editor/Actions/ActionRuntimeTest.cs.meta new file mode 100644 index 0000000..a11a352 --- /dev/null +++ b/Tests/Editor/Actions/ActionRuntimeTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 14857be8115941db8a6c1d94a3172dfe +timeCreated: 1560977894 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases.meta b/Tests/Editor/Actions/Databases.meta new file mode 100644 index 0000000..c170aaf --- /dev/null +++ b/Tests/Editor/Actions/Databases.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50918ab842e8401c8a2bc1920c65a829 +timeCreated: 1562004554 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs b/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs new file mode 100644 index 0000000..047b173 --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs @@ -0,0 +1,52 @@ +using CleverCrow.Fluid.Databases; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public class ConditionLocalBoolTest { + private ConditionBoolInternal _boolCondition; + private IKeyValueDefinition _definition; + private IKeyValueData _database; + + [SetUp] + public void BeforeEach () { + _definition = Substitute.For>(); + _database = Substitute.For>(); + _boolCondition = new ConditionBoolInternal(_database); + + _database.Get(null).ReturnsForAnyArgs(true); + } + + public class AreValuesEqualMethod : ConditionLocalBoolTest { + [Test] + public void It_should_return_true_if_variable_and_value_are_equal () { + var result = _boolCondition.AreValuesEqual(_definition, true); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_variable_and_value_are_not_equal () { + var result = _boolCondition.AreValuesEqual(_definition, false); + + Assert.IsFalse(result); + } + } + + public class AreValuesNotEqualMethod : ConditionLocalBoolTest { + [Test] + public void It_should_return_true_if_variable_and_value_are_not_equal () { + var result = _boolCondition.AreValuesNotEqual(_definition, false); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_variable_and_value_are_equal () { + var result = _boolCondition.AreValuesNotEqual(_definition, true); + + Assert.IsFalse(result); + } + } + } +} diff --git a/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs.meta b/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs.meta new file mode 100644 index 0000000..dfae174 --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalBoolTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d908d1eff814d6080335e774b6602e3 +timeCreated: 1561943627 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs b/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs new file mode 100644 index 0000000..444c00e --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs @@ -0,0 +1,132 @@ +using CleverCrow.Fluid.Databases; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public class ConditionLocalFloatTest { + private ConditionFloatInternal _condition; + private IKeyValueDefinition _definition; + private IKeyValueData _database; + + [SetUp] + public void BeforeEach () { + _definition = Substitute.For>(); + _database = Substitute.For>(); + _condition = new ConditionFloatInternal(_database); + + _database.Get(null).ReturnsForAnyArgs(0); + } + + public class IsComparisonValidMethod { + public class ComparisonEqualEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_for_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.Equal); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_for_not_equal () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.Equal); + + Assert.IsFalse(result); + } + } + + public class ComparisonNotEqualEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_for_not_equal () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.NotEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_for_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.NotEqual); + + Assert.IsFalse(result); + } + } + + public class ComparisonGreaterThanEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.GreaterThan); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.GreaterThan); + + Assert.IsFalse(result); + } + } + + public class ComparisonLessThanEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.LessThan); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.LessThan); + + Assert.IsFalse(result); + } + } + + public class ComparisonGreaterThanOrEqualEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.GreaterThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_true_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.GreaterThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.GreaterThanOrEqual); + + Assert.IsFalse(result); + } + } + + public class ComparisonLessThanOrEqualEnum : ConditionLocalFloatTest { + [Test] + public void It_should_return_true_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.LessThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_true_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.LessThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.LessThanOrEqual); + + Assert.IsFalse(result); + } + } + } + } +} diff --git a/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs.meta b/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs.meta new file mode 100644 index 0000000..8999e57 --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalFloatTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e975aabd920b442fa1f52bc6dab0ec01 +timeCreated: 1562039346 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs b/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs new file mode 100644 index 0000000..4b6cbef --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs @@ -0,0 +1,132 @@ +using CleverCrow.Fluid.Databases; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public class ConditionLocalIntTest { + private ConditionIntInternal _condition; + private IKeyValueDefinition _definition; + private IKeyValueData _database; + + [SetUp] + public void BeforeEach () { + _definition = Substitute.For>(); + _database = Substitute.For>(); + _condition = new ConditionIntInternal(_database); + + _database.Get(null).ReturnsForAnyArgs(0); + } + + public class IsComparisonValidMethod { + public class ComparisonEqualEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_for_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.Equal); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_for_not_equal () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.Equal); + + Assert.IsFalse(result); + } + } + + public class ComparisonNotEqualEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_for_not_equal () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.NotEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_for_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.NotEqual); + + Assert.IsFalse(result); + } + } + + public class ComparisonGreaterThanEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.GreaterThan); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.GreaterThan); + + Assert.IsFalse(result); + } + } + + public class ComparisonLessThanEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.LessThan); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.LessThan); + + Assert.IsFalse(result); + } + } + + public class ComparisonGreaterThanOrEqualEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.GreaterThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_true_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.GreaterThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.GreaterThanOrEqual); + + Assert.IsFalse(result); + } + } + + public class ComparisonLessThanOrEqualEnum : ConditionLocalIntTest { + [Test] + public void It_should_return_true_if_less_than () { + var result = _condition.IsComparisonValid(_definition, -1, NumberComparison.LessThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_true_if_equal () { + var result = _condition.IsComparisonValid(_definition, 0, NumberComparison.LessThanOrEqual); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_greater_than () { + var result = _condition.IsComparisonValid(_definition, 1, NumberComparison.LessThanOrEqual); + + Assert.IsFalse(result); + } + } + } + } +} diff --git a/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs.meta b/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs.meta new file mode 100644 index 0000000..b1b7eae --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalIntTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 857431eb23474ed79e99d3e6006d2840 +timeCreated: 1562008472 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs b/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs new file mode 100644 index 0000000..a1126d8 --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs @@ -0,0 +1,54 @@ +using CleverCrow.Fluid.Databases; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public class ConditionLocalStringTest { + private const string VALUE = "a"; + + private ConditionStringInternal _condition; + private IKeyValueDefinition _definition; + private IKeyValueData _database; + + [SetUp] + public void BeforeEach () { + _definition = Substitute.For>(); + _database = Substitute.For>(); + _condition = new ConditionStringInternal(_database); + + _database.Get(null).ReturnsForAnyArgs(VALUE); + } + + public class AreValuesEqualMethod : ConditionLocalStringTest { + [Test] + public void It_should_return_true_if_variable_and_value_are_equal () { + var result = _condition.AreValuesEqual(_definition, VALUE); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_variable_and_value_are_not_equal () { + var result = _condition.AreValuesEqual(_definition, "b"); + + Assert.IsFalse(result); + } + } + + public class AreValuesNotEqualMethod : ConditionLocalStringTest { + [Test] + public void It_should_return_true_if_variable_and_value_are_not_equal () { + var result = _condition.AreValuesNotEqual(_definition, "b"); + + Assert.IsTrue(result); + } + + [Test] + public void It_should_return_false_if_variable_and_value_are_equal () { + var result = _condition.AreValuesNotEqual(_definition, VALUE); + + Assert.IsFalse(result); + } + } + } +} diff --git a/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs.meta b/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs.meta new file mode 100644 index 0000000..4bc9495 --- /dev/null +++ b/Tests/Editor/Actions/Databases/ConditionLocalStringTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 98c52dccda954763828dc24330831375 +timeCreated: 1562004605 \ No newline at end of file diff --git a/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs b/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs new file mode 100644 index 0000000..8f5d23f --- /dev/null +++ b/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs @@ -0,0 +1,22 @@ +using CleverCrow.Fluid.Databases; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Actions.Databases { + public class SetLocalBoolTest { + public class WriteValueMethod { + [Test] + public void It_should_set_the_local_database_value () { + const string KEY = "key"; + const bool VALUE = true; + + var database = Substitute.For>(); + var setter = new SetKeyValueInternal(database); + + setter.WriteValue(KEY, VALUE); + + database.Received(1).Set(KEY, VALUE); + } + } + } +} diff --git a/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs.meta b/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs.meta new file mode 100644 index 0000000..173d24b --- /dev/null +++ b/Tests/Editor/Actions/Databases/SetLocalBoolTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: de278d5198ae4d2abdadb3389c3a0c90 +timeCreated: 1561928232 \ No newline at end of file diff --git a/Tests/Editor/Builders.meta b/Tests/Editor/Builders.meta new file mode 100644 index 0000000..051046a --- /dev/null +++ b/Tests/Editor/Builders.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d707c001372a44e5bfb90dd27b11f99c +timeCreated: 1560545993 \ No newline at end of file diff --git a/Tests/Editor/Builders/A.cs b/Tests/Editor/Builders/A.cs new file mode 100644 index 0000000..d7fb796 --- /dev/null +++ b/Tests/Editor/Builders/A.cs @@ -0,0 +1,9 @@ +namespace CleverCrow.Fluid.Dialogues.Builders { + public static class A { + public static DialogueGraphStubBuilder Graph => new DialogueGraphStubBuilder(); + public static DialogueNodeStubBuilder Node => new DialogueNodeStubBuilder(); + public static NodeDataStubBuilder NodeData => new NodeDataStubBuilder(); + public static ActionStubBuilder Action => new ActionStubBuilder(); + public static ChoiceStubBuilder Choice => new ChoiceStubBuilder(); + } +} diff --git a/Tests/Editor/Builders/A.cs.meta b/Tests/Editor/Builders/A.cs.meta new file mode 100644 index 0000000..fc9749e --- /dev/null +++ b/Tests/Editor/Builders/A.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2552ff253ee742429f5b0904ba81f599 +timeCreated: 1560545981 \ No newline at end of file diff --git a/Tests/Editor/Builders/ActionStubBuilder.cs b/Tests/Editor/Builders/ActionStubBuilder.cs new file mode 100644 index 0000000..ea6550f --- /dev/null +++ b/Tests/Editor/Builders/ActionStubBuilder.cs @@ -0,0 +1,20 @@ +using CleverCrow.Fluid.Dialogues.Actions; +using NSubstitute; + +namespace CleverCrow.Fluid.Dialogues.Builders { + public class ActionStubBuilder { + private ActionStatus _tickStatus = ActionStatus.Success; + + public ActionStubBuilder WithTickStatus (ActionStatus status) { + _tickStatus = status; + return this; + } + + public IAction Build () { + var action = Substitute.For(); + action.Tick().Returns(_tickStatus); + + return action; + } + } +} diff --git a/Tests/Editor/Builders/ActionStubBuilder.cs.meta b/Tests/Editor/Builders/ActionStubBuilder.cs.meta new file mode 100644 index 0000000..bf0c586 --- /dev/null +++ b/Tests/Editor/Builders/ActionStubBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d59954ff3ac54a9da177f8fad414e4cb +timeCreated: 1560803581 \ No newline at end of file diff --git a/Tests/Editor/Builders/ChoiceStubBuilder.cs b/Tests/Editor/Builders/ChoiceStubBuilder.cs new file mode 100644 index 0000000..ff6c81a --- /dev/null +++ b/Tests/Editor/Builders/ChoiceStubBuilder.cs @@ -0,0 +1,34 @@ +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; + +namespace CleverCrow.Fluid.Dialogues.Builders { + public class ChoiceStubBuilder { + private bool _overrideChildNode; + private INode _validChildNode; + + private bool _isValid = true; + + public ChoiceStubBuilder WithIsValid (bool isValid) { + _isValid = isValid; + return this; + } + + public ChoiceStubBuilder WithValidChildNode (INode validChildNode) { + _overrideChildNode = true; + _validChildNode = validChildNode; + return this; + } + + public IChoice Build () { + var choice = Substitute.For(); + choice.IsValid.Returns(_isValid); + + if (_overrideChildNode) { + choice.GetValidChildNode().Returns(_validChildNode); + } + + return choice; + } + } +} diff --git a/Tests/Editor/Builders/ChoiceStubBuilder.cs.meta b/Tests/Editor/Builders/ChoiceStubBuilder.cs.meta new file mode 100644 index 0000000..5a619e0 --- /dev/null +++ b/Tests/Editor/Builders/ChoiceStubBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 65975b0c866a4e5294d50bd2f706ea6f +timeCreated: 1566058101 \ No newline at end of file diff --git a/Tests/Editor/Builders/DialogueGraphStubBuilder.cs b/Tests/Editor/Builders/DialogueGraphStubBuilder.cs new file mode 100644 index 0000000..e9fc282 --- /dev/null +++ b/Tests/Editor/Builders/DialogueGraphStubBuilder.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; + +namespace CleverCrow.Fluid.Dialogues.Builders { + public class DialogueGraphStubBuilder { + private readonly List _nodes = new List(); + private INode _next; + + public DialogueGraphStubBuilder WithNode (INodeData node) { + _nodes.Add(node); + return this; + } + + public DialogueGraphStubBuilder WithNextResult (INode node) { + _next = node; + return this; + } + + public IGraph Build () { + var graph = Substitute.For(); + var root = A.Node + .WithNextResult(_next) + .WithPlayAction((playback) => playback.Next()) + .Build(); + graph.Root.Returns(root); + + var rootData = A.NodeData + .WithNode(root) + .Build(); + + graph.GetCopy(rootData).Returns(root); + + foreach (var nodeData in _nodes) { + graph + .GetCopy(nodeData) + .Returns(x => nodeData.GetRuntime(null, null)); + } + + return graph; + } + } +} diff --git a/Tests/Editor/Builders/DialogueGraphStubBuilder.cs.meta b/Tests/Editor/Builders/DialogueGraphStubBuilder.cs.meta new file mode 100644 index 0000000..5b9eb07 --- /dev/null +++ b/Tests/Editor/Builders/DialogueGraphStubBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af85b0b1d4db4e96b15be923a987352c +timeCreated: 1560546007 \ No newline at end of file diff --git a/Tests/Editor/Builders/DialogueNodeStubBuilder.cs b/Tests/Editor/Builders/DialogueNodeStubBuilder.cs new file mode 100644 index 0000000..bc242a7 --- /dev/null +++ b/Tests/Editor/Builders/DialogueNodeStubBuilder.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; + +namespace CleverCrow.Fluid.Dialogues.Builders { + public class DialogueNodeStubBuilder { + private INode _next; + private readonly List _exitActions = new List(); + private readonly List _enterActions = new List(); + private readonly List _choices = new List(); + private bool _isValid = true; + private INode _clone; + private readonly List _hubChoices = new List(); + private Action _playAction = null; + + public DialogueNodeStubBuilder WithNextResult (INode node) { + _next = node; + return this; + } + + public DialogueNodeStubBuilder WithEnterAction (IAction action) { + _enterActions.Add(action); + return this; + } + + public DialogueNodeStubBuilder WithExitAction (IAction action) { + _exitActions.Add(action); + return this; + } + + public DialogueNodeStubBuilder WithChoice (IChoice choice) { + _choices.Add(choice); + return this; + } + + public DialogueNodeStubBuilder WithIsValid (bool valid) { + _isValid = valid; + return this; + } + + public DialogueNodeStubBuilder WithHubChoice (IChoice choice) { + _hubChoices.Add(choice); + return this; + } + + public DialogueNodeStubBuilder WithPlayAction (Action action) { + _playAction = action; + return this; + } + + public INode Build () { + var node = Substitute.For(); + node.Next().Returns(_next); + node.ExitActions.Returns(_exitActions); + node.EnterActions.Returns(_enterActions); + node.IsValid.Returns(_isValid); + node.HubChoices.Returns(_hubChoices); + + node.WhenForAnyArgs(x => x.Play(null)) + .Do((x) => { + if (_playAction == null) return; + var playback = (DialoguePlayback)x[0]; + _playAction(playback); + }); + + for (var i = 0; i < _choices.Count; i++) { + node.GetChoice(i).Returns(_choices[i]); + } + + return node; + } + } +} diff --git a/Tests/Editor/Builders/DialogueNodeStubBuilder.cs.meta b/Tests/Editor/Builders/DialogueNodeStubBuilder.cs.meta new file mode 100644 index 0000000..41ce839 --- /dev/null +++ b/Tests/Editor/Builders/DialogueNodeStubBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1105f9dfcbf746b8a2d6fd16f5779dec +timeCreated: 1560546092 \ No newline at end of file diff --git a/Tests/Editor/Builders/NodeDataStubBuilder.cs b/Tests/Editor/Builders/NodeDataStubBuilder.cs new file mode 100644 index 0000000..7b7a3ee --- /dev/null +++ b/Tests/Editor/Builders/NodeDataStubBuilder.cs @@ -0,0 +1,20 @@ +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; + +namespace CleverCrow.Fluid.Dialogues.Builders { + public class NodeDataStubBuilder { + private INode _node; + + public NodeDataStubBuilder WithNode (INode node) { + _node = node; + return this; + } + + public INodeData Build () { + var nodeData = Substitute.For(); + nodeData.GetRuntime(null, null).ReturnsForAnyArgs(_node); + + return nodeData; + } + } +} diff --git a/Tests/Editor/Builders/NodeDataStubBuilder.cs.meta b/Tests/Editor/Builders/NodeDataStubBuilder.cs.meta new file mode 100644 index 0000000..f9d6b36 --- /dev/null +++ b/Tests/Editor/Builders/NodeDataStubBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 085928b446694f10b7561130d685ee48 +timeCreated: 1565991122 \ No newline at end of file diff --git a/Tests/Editor/ChoiceRuntimeTest.cs b/Tests/Editor/ChoiceRuntimeTest.cs new file mode 100644 index 0000000..4d19009 --- /dev/null +++ b/Tests/Editor/ChoiceRuntimeTest.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Nodes; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Choices.Tests { + public class ChoiceRuntimeTest { + public class IsValidProperty { + [Test] + public void It_should_return_true_if_children_is_empty () { + var children = new List(); + var graph = A.Graph.Build(); + var choice = new ChoiceRuntime(graph, "", null, children); + + Assert.IsTrue(choice.IsValid); + } + + [Test] + public void It_should_return_true_if_a_valid_child_is_available () { + var node = A.Node + .WithIsValid(true) + .Build(); + var nodeData = A.NodeData.WithNode(node).Build(); + var children = new List { nodeData }; + var graph = A.Graph.WithNode(nodeData).Build(); + var choice = new ChoiceRuntime(graph, "", null, children); + + Assert.IsTrue(choice.IsValid); + } + + [Test] + public void It_should_return_false_if_an_invalid_child_is_only_available () { + var node = A.Node + .WithIsValid(false) + .Build(); + var nodeData = A.NodeData.WithNode(node).Build(); + var children = new List { nodeData }; + var graph = A.Graph.WithNode(nodeData).Build(); + var choice = new ChoiceRuntime(graph, "", null, children); + + Assert.IsFalse(choice.IsValid); + } + } + + public class GetValidChildNodeMethod { + [Test] + public void It_should_return_a_valid_child () { + var node = A.Node.Build(); + var nodeData = A.NodeData.WithNode(node).Build(); + var children = new List { nodeData }; + var graph = A.Graph.WithNode(nodeData).Build(); + var choice = new ChoiceRuntime(graph, "", null, children); + + var result = choice.GetValidChildNode(); + + Assert.AreEqual(node, result); + } + + [Test] + public void It_should_not_return_an_invalid_child () { + var node = A.Node.WithIsValid(false).Build(); + var nodeData = A.NodeData.WithNode(node).Build(); + var children = new List { nodeData }; + var graph = A.Graph.WithNode(nodeData).Build(); + var choice = new ChoiceRuntime(graph, "", null, children); + + var result = choice.GetValidChildNode(); + + Assert.IsNull(result); + } + } + } +} diff --git a/Tests/Editor/ChoiceRuntimeTest.cs.meta b/Tests/Editor/ChoiceRuntimeTest.cs.meta new file mode 100644 index 0000000..dea06e2 --- /dev/null +++ b/Tests/Editor/ChoiceRuntimeTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 313cd24e9d404b6990cebc70187d9256 +timeCreated: 1560975758 \ No newline at end of file diff --git a/Tests/Editor/ConditionRuntimeTest.cs b/Tests/Editor/ConditionRuntimeTest.cs new file mode 100644 index 0000000..86dec30 --- /dev/null +++ b/Tests/Editor/ConditionRuntimeTest.cs @@ -0,0 +1,49 @@ +using CleverCrow.Fluid.Dialogues.Conditions; +using NSubstitute; +using NUnit.Framework; + +namespace FluidDialogue.Tests.Editor { + public class ConditionRuntimeTest { + private IConditionData _data; + + [SetUp] + public void Setup () { + _data = Substitute.For(); + } + + public class GetIsValidMethod { + public class OnGetIsValidTriggering : ConditionRuntimeTest { + [Test] + public void It_should_return_the_OnGetIsValid_value () { + _data.OnGetIsValid(null).Returns(true); + var condition = new ConditionRuntime(null, null, _data); + + var result = condition.GetIsValid(null); + + Assert.IsTrue(result); + } + } + + public class OnInitTriggering : ConditionRuntimeTest { + [Test] + public void It_should_trigger_OnInit_with_a_dialogue_controller () { + var condition = new ConditionRuntime(null, null, _data); + + condition.GetIsValid(null); + + _data.Received(1).OnInit(null); + } + + [Test] + public void It_should_trigger_OnInit_only_once () { + var condition = new ConditionRuntime(null, null, _data); + + condition.GetIsValid(null); + condition.GetIsValid(null); + + _data.Received(1).OnInit(null); + } + } + } + } +} diff --git a/Tests/Editor/ConditionRuntimeTest.cs.meta b/Tests/Editor/ConditionRuntimeTest.cs.meta new file mode 100644 index 0000000..5f08d7c --- /dev/null +++ b/Tests/Editor/ConditionRuntimeTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 72132a51ffe94cdcba6e79bc1a8674c9 +timeCreated: 1561067131 \ No newline at end of file diff --git a/Tests/Editor/DialogueControllerTest.cs b/Tests/Editor/DialogueControllerTest.cs new file mode 100644 index 0000000..003ffd7 --- /dev/null +++ b/Tests/Editor/DialogueControllerTest.cs @@ -0,0 +1,309 @@ +using System; +using CleverCrow.Fluid.Databases; +using CleverCrow.Fluid.Dialogues.Builders; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues { + public class DialogueControllerTest { + public class WithLocalDatabase { + private DialogueController _ctrl; + private IDialoguePlayback _playback; + private IDatabaseInstance _database; + + [SetUp] + public void BeforeEach () { + _database = Substitute.For(); +#pragma warning disable 618 + _ctrl = new DialogueController(_database); +#pragma warning restore 618 + _playback = Substitute.For(); + } + + public class PlayMethod : WithLocalDatabase { + [Test] + public void It_should_run_play_on_the_graph () { + _ctrl.Play(_playback); + + _playback.Received(1).Play(); + } + + [Test] + public void It_should_call_Stop_on_already_running_dialogue () { + _ctrl.Play(_playback); + _ctrl.Play(_playback); + + _playback.Received(1).Stop(); + } + + [Test] + public void It_should_bind_the_begin_event () { + var beginResult = false; + _ctrl.Events.Begin.AddListener(() => beginResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(playback); + + Assert.IsTrue(beginResult); + } + + [Test] + public void It_should_bind_the_end_event () { + var endResult = false; + _ctrl.Events.End.AddListener(() => endResult = true); + var playbackEmpty = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(playbackEmpty); + + Assert.IsTrue(endResult); + } + + [Test] + public void It_should_bind_dialogue_speak_events () { + var speakResult = false; + _ctrl.Events.Speak.AddListener((x, y) => speakResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(playback); + playback.Events.Speak.Invoke(null, null); + + Assert.IsTrue(speakResult); + } + + [Test] + public void It_should_bind_dialogue_speak_with_audio_events () { + var speakResult = false; + _ctrl.Events.SpeakWithAudio.AddListener((x, y, _) => speakResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(playback); + playback.Events.SpeakWithAudio.Invoke(null, null, null); + + Assert.IsTrue(speakResult); + } + + [Test] + public void It_should_bind_dialogue_choice_events () { + var choiceResult = false; + _ctrl.Events.Choice.AddListener((x, y, z) => choiceResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(playback); + playback.Events.Choice.Invoke(null, null, null); + + Assert.IsTrue(choiceResult); + } + + [Test] + public void It_should_reset_the_local_database () { + _ctrl.Play(_playback); + + _database.Received(1).Clear(); + } + } + + public class PlayChildMethod { + public class ErrorHandling : WithLocalDatabase { + [Test] + public void It_should_throw_an_error_if_nothing_is_playing () { + Assert.Throws( + () => _ctrl.PlayChild(_playback), "Cannot trigger child dialogue, nothing is playing"); + } + } + + public class SuccessfulRuns : WithLocalDatabase { + private IDialoguePlayback _parentPlayback; + + [SetUp] + public void BeforeEachMethod () { + _parentPlayback = Substitute.For(); + _ctrl.Play(_parentPlayback); + } + + [Test] + public void It_should_call_Play_on_the_dialogue () { + _ctrl.PlayChild(_playback); + + _playback.Received(1).Play(); + } + + [Test] + public void It_should_bind_the_speak_event () { + var speakResult = false; + _ctrl.Events.Speak.AddListener((x, y) => speakResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.PlayChild(playback); + playback.Events.Speak.Invoke(null, null); + + Assert.IsTrue(speakResult); + } + + [Test] + public void It_should_bind_the_speak_with_audio_event () { + var speakResult = false; + _ctrl.Events.SpeakWithAudio.AddListener((x, y, _) => speakResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.PlayChild(playback); + playback.Events.SpeakWithAudio.Invoke(null, null, null); + + Assert.IsTrue(speakResult); + } + + [Test] + public void It_should_bind_the_choice_event () { + var choiceResult = false; + _ctrl.Events.Choice.AddListener((x, y, z) => choiceResult = true); + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.PlayChild(playback); + playback.Events.Choice.Invoke(null, null, null); + + Assert.IsTrue(choiceResult); + } + + [Test] + public void It_should_call_Next_on_the_parent_if_End_is_called () { + var playback = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + // Automatically calls End since the graph is empty + _ctrl.PlayChild(playback); + + _parentPlayback.Received(1).Next(); + } + } + } + + public class ActiveDialogueProperty : WithLocalDatabase { + [Test] + public void It_should_return_the_playing_graph () { + _ctrl.Play(_playback); + + Assert.AreEqual(_playback, _ctrl.ActiveDialogue); + } + + [Test] + public void It_should_clear_if_playing_graph_calls_end () { + var dialogueEmpty = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(dialogueEmpty); + + Assert.AreEqual(null, _ctrl.ActiveDialogue); + } + + [Test] + public void It_should_return_PlayChild_after_play_is_called () { + var dialogueChild = Substitute.For(); + + _ctrl.Play(_playback); + _ctrl.PlayChild(dialogueChild); + + Assert.AreEqual(dialogueChild, _ctrl.ActiveDialogue); + } + + [Test] + public void It_should_return_Play_dialogue_if_PlayChild_dialogue_calls_end_event () { + var dialogueEmpty = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(_playback); + _ctrl.PlayChild(dialogueEmpty); + + Assert.AreEqual(_playback, _ctrl.ActiveDialogue); + } + + [Test] + public void It_should_restore_the_parent_child_if_a_nested_child_ends () { + var dialogueChild = Substitute.For(); + var dialogueEmpty = new DialoguePlayback(A.Graph.Build(), null, new DialogueEvents()); + + _ctrl.Play(_playback); + _ctrl.PlayChild(dialogueChild); + _ctrl.PlayChild(dialogueEmpty); + + Assert.AreEqual(dialogueChild, _ctrl.ActiveDialogue); + } + } + + public class NextMethod : WithLocalDatabase { + [Test] + public void It_should_do_nothing_if_no_current_playback () { + Assert.DoesNotThrow(() => _ctrl.Next()); + } + + [Test] + public void It_should_call_Next_on_current_playback () { + _ctrl.Play(_playback); + _ctrl.Next(); + + _playback.Received(1).Next(); + } + } + + public class TickMethod : WithLocalDatabase { + [Test] + public void It_should_do_nothing_if_no_current_playback () { + Assert.DoesNotThrow(() => _ctrl.Tick()); + } + + [Test] + public void It_should_call_Tick_on_current_playback () { + _ctrl.Play(_playback); + _ctrl.Tick(); + + _playback.Received(1).Tick(); + } + } + + public class SelectChoice : WithLocalDatabase { + [Test] + public void It_should_do_nothing_if_no_current_playback () { + Assert.DoesNotThrow(() => _ctrl.SelectChoice(0)); + } + + [Test] + public void It_should_call_SelectChoice_on_current_playback () { + _ctrl.Play(_playback); + _ctrl.SelectChoice(0); + + _playback.Received(1).SelectChoice(0); + } + } + + public class StopMethod : WithLocalDatabase { + [Test] + public void It_should_do_nothing_if_no_current_playback () { + Assert.DoesNotThrow(() => _ctrl.Stop()); + } + + [Test] + public void It_should_run_stop_on_active_dialogue () { + _ctrl.Play(_playback); + _ctrl.Stop(); + + _playback.Received(1).Stop(); + } + + [Test] + public void It_should_run_stop_on_multiple_active_dialogues () { + var playbackChild = Substitute.For(); + + _ctrl.Play(_playback); + _ctrl.PlayChild(playbackChild); + _ctrl.Stop(); + + _playback.Received(1).Stop(); + playbackChild.Received(1).Stop(); + } + + [Test] + public void It_should_clear_ActiveDialogue () { + _ctrl.Play(_playback); + _ctrl.Stop(); + + Assert.IsNull(_ctrl.ActiveDialogue); + } + } + } + } +} diff --git a/Tests/Editor/DialogueControllerTest.cs.meta b/Tests/Editor/DialogueControllerTest.cs.meta new file mode 100644 index 0000000..ed4f97b --- /dev/null +++ b/Tests/Editor/DialogueControllerTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 39100e9d49f942c1b833c9432e434d69 +timeCreated: 1561159830 \ No newline at end of file diff --git a/Tests/Editor/DialoguePlaybackTest.cs b/Tests/Editor/DialoguePlaybackTest.cs new file mode 100644 index 0000000..7eccf04 --- /dev/null +++ b/Tests/Editor/DialoguePlaybackTest.cs @@ -0,0 +1,392 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues { + public class DialoguePlaybackTest { + private DialoguePlayback _playback; + private IGraph _graph; + private IDialogueEvents _events; + + [SetUp] + public void BeforeEach () { + _graph = A.Graph + .WithNextResult(A.Node.Build()) + .Build(); + + _events = Substitute.For(); + _playback = new DialoguePlayback(_graph, null, _events); + } + + public class PlayMethod { + public class Defaults : DialoguePlaybackTest { + [Test] + public void It_should_trigger_a_Begin_event () { + _playback.Play(); + + _playback.Events.Begin.Received(1).Invoke(); + } + + [Test] + public void It_should_trigger_a_speak_event_with_the_root_child_dialogue () { + var node = A.Node.Build(); + node.Play(Arg.Do(p => p.Events.Speak.Invoke(null, null))); + + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + + _playback.Events.Speak.ReceivedWithAnyArgs(1).Invoke(null, null); + } + + [Test] + public void It_should_trigger_a_speak_with_audio_event_with_the_root_child_dialogue () { + var node = A.Node.Build(); + node.Play(Arg.Do(p => p.Events.SpeakWithAudio.Invoke(null, null, null))); + + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + + _playback.Events.SpeakWithAudio.ReceivedWithAnyArgs(1).Invoke(null, null, null); + } + + [Test] + public void It_should_trigger_play_on_the_root_child () { + var node = A.Node.Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + + node.Received(1).Play(_playback); + } + } + + public class RootNode : DialoguePlaybackTest { + [Test] + public void It_should_trigger_play_on_the_root_node () { + _playback.Play(); + + _graph.Root.Received(1).Play(_playback); + } + + [Test] + public void It_should_trigger_end_event_if_root_isValid_is_false () { + _graph.Root.IsValid.Returns(false); + + _playback.Play(); + + _playback.Events.End.Received(1).Invoke(); + } + } + + public class RootEnterActions : DialoguePlaybackTest { + private IAction _action; + + [SetUp] + public void BeforeEachMethod () { + _action = A.Action + .WithTickStatus(ActionStatus.Continue) + .Build(); + _graph.Root.EnterActions.Returns(new List {_action}); + } + + [Test] + public void It_should_run_root_enter_actions () { + _playback.Play(); + + _action.Received(1).Tick(); + } + + [Test] + public void It_should_call_begin_event_on_action_failure () { + _playback.Play(); + + _playback.Events.Begin.Received(1).Invoke(); + } + + [Test] + public void If_root_enter_action_returns_false_do_not_call_speak () { + var node = A.Node.Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _graph.Root.EnterActions.Returns(new List {_action}); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + + node.DidNotReceive().Play(_playback); + } + + [Test] + public void Calling_Play_a_second_time_should_call_end_on_all_active_actions () { + _playback.Play(); + _playback.Play(); + + _action.Received(1).End(); + } + } + } + + public class NextMethod { + public class Defaults : DialoguePlaybackTest { + [Test] + public void It_should_trigger_speak_on_the_next_sibling_node () { + var nodeNested = A.Node.Build(); + var node = A.Node + .WithNextResult(nodeNested) + .Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + _playback.Next(); + + nodeNested.Received(1).Play(_playback); + } + + [Test] + public void If_no_next_result_trigger_end_event () { + var node = A.Node + .WithNextResult(null) + .Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + _playback.Next(); + + _playback.Events.End.Received(1).Invoke(); + } + + [Test] + public void It_should_trigger_end_event_only_once_when_an_action_is_present () { + var action = A.Action.Build(); + var node = A.Node + .WithNextResult(null) + .WithEnterAction(action) + .Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + _playback.Next(); + + _playback.Events.End.Received(1).Invoke(); + } + } + + public class ChoiceHandling : DialoguePlaybackTest { + [Test] + public void It_should_emit_a_choice_event_if_next_node_has_choices () { + var choice = Substitute.For(); + var nodeNested = A.Node + .WithChoice(choice) + .Build(); + var node = A.Node + .WithNextResult(nodeNested) + .Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + _playback.Next(); + + nodeNested.Received(1).Play(_playback); + } + } + + public class ExitActions : DialoguePlaybackTest { + private IAction _exitAction; + private INode _node; + private INode _nodeNested; + + [SetUp] + public void BeforeEachMethod () { + _exitAction = A.Action + .WithTickStatus(ActionStatus.Continue) + .Build(); + _nodeNested = A.Node.Build(); + _node = A.Node + .WithExitAction(_exitAction) + .WithNextResult(_nodeNested) + .Build(); + _graph = A.Graph + .WithNextResult(_node) + .Build(); + + _playback = new DialoguePlayback(_graph, null, _events); + _playback.Play(); + _playback.Next(); + } + + [Test] + public void It_should_trigger_all_exit_actions_on_current () { + _exitAction.Received(1).Tick(); + } + + [Test] + public void It_should_not_trigger_Speak_on_the_next_node_if_an_IAction_Update_returns_false () { + _nodeNested.DidNotReceive().Play(_playback); + } + + [Test] + public void It_should_trigger_Speak_on_the_next_node_if_an_IAction_Update_returns_false_then_true () { + _exitAction.Tick().Returns(ActionStatus.Success); + _playback.Tick(); + + _nodeNested.Received(1).Play(_playback); + } + + [Test] + public void It_should_do_nothing_if_actions_are_running () { + _playback.Next(); + + _exitAction.Received(1).Tick(); + } + } + + public class EnterActions : DialoguePlaybackTest { + private IAction _enterAction; + private INode _node; + + [SetUp] + public void BeforeEachMethod () { + _enterAction = A.Action + .WithTickStatus(ActionStatus.Continue) + .Build(); + _node = A.Node + .WithEnterAction(_enterAction) + .Build(); + _graph = A.Graph + .WithNextResult(_node) + .Build(); + + _playback = new DialoguePlayback(_graph, null, _events); + } + + [Test] + public void It_should_trigger_all_enter_actions_on_current () { + _playback.Play(); + _playback.Next(); + + _enterAction.Received(1).Tick(); + } + } + } + + public class TickMethod : DialoguePlaybackTest { + [Test] + public void It_should_not_crash_when_updating_multiple_actions_at_the_same_time () { + var action = A.Action + .WithTickStatus(ActionStatus.Continue) + .Build(); + var node = A.Node + .WithExitAction(action) + .WithExitAction(action) + .Build(); + var graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(graph, null, _events); + + _playback.Play(); + _playback.Next(); + action.Tick().Returns(ActionStatus.Success); + _playback.Tick(); + } + } + + public class StopMethod : DialoguePlaybackTest { + private IAction _action; + + [SetUp] + public void BeforeEachMethod () { + _action = A.Action + .WithTickStatus(ActionStatus.Continue) + .Build(); + _graph.Root.EnterActions.Returns(new List {_action}); + } + + [Test] + public void It_should_call_end_on_all_active_running_actions () { + _playback.Play(); + _playback.Stop(); + + _action.Received(1).End(); + } + + [Test] + public void It_should_clear_the_pointer () { + _playback.Play(); + _playback.Stop(); + + Assert.IsNull(_playback.Pointer); + } + + [Test] + public void It_should_call_End_event () { + _playback.Play(); + _playback.Stop(); + + _playback.Events.End.Received(1).Invoke(); + } + + [Test] + public void It_should_not_call_End_event_if_Play_was_not_called () { + _playback.Stop(); + + _playback.Events.End.DidNotReceive().Invoke(); + } + } + + public class SelectChoiceMethod : DialoguePlaybackTest { + [Test] + public void It_should_trigger_the_current_pointer_choice_to_speak () { + var choiceNode = A.Node.Build(); + var choice = Substitute.For(); + choice.GetValidChildNode().Returns(choiceNode); + + var nodeNested = A.Node + .WithChoice(choice) + .Build(); + var node = A.Node + .WithNextResult(nodeNested) + .Build(); + _graph = A.Graph + .WithNextResult(node) + .Build(); + _playback = new DialoguePlayback(_graph, null, _events); + + _playback.Play(); + _playback.Next(); + _playback.SelectChoice(0); + + choiceNode.Received(1).Play(_playback); + } + } + } +} diff --git a/Tests/Editor/DialoguePlaybackTest.cs.meta b/Tests/Editor/DialoguePlaybackTest.cs.meta new file mode 100644 index 0000000..4443cf1 --- /dev/null +++ b/Tests/Editor/DialoguePlaybackTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc06da1936b2432e945e56d7b98b671d +timeCreated: 1560528721 \ No newline at end of file diff --git a/Tests/Editor/GraphRuntimeTest.cs b/Tests/Editor/GraphRuntimeTest.cs new file mode 100644 index 0000000..1b877e8 --- /dev/null +++ b/Tests/Editor/GraphRuntimeTest.cs @@ -0,0 +1,45 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes; +using NSubstitute; +using NUnit.Framework; +using System.Collections.Generic; + +namespace FluidDialogue.Tests.Editor { + public class GraphRuntimeTest { + public class Constructor { + [Test] + public void It_should_set_the_root_to_the_root_runtime_value () { + var root = Substitute.For(); + var rootCopy = Substitute.For(); + root.GetRuntime(null, null).ReturnsForAnyArgs(rootCopy); + + var graphData = Substitute.For(); + IReadOnlyList nodeList = new List {root}; + graphData.Nodes.Returns(nodeList); + graphData.Root.Returns(root); + + var graph = new GraphRuntime(null, graphData); + + Assert.AreEqual(rootCopy, graph.Root); + } + } + + public class GetCopyMethod { + [Test] + public void It_should_return_a_copy_of_the_original () { + var root = Substitute.For(); + var rootCopy = Substitute.For(); + root.GetRuntime(null, null).ReturnsForAnyArgs(rootCopy); + + var graphData = Substitute.For(); + IReadOnlyList nodeList = new List {root}; + graphData.Nodes.Returns(nodeList); + graphData.Root.Returns(root); + + var graph = new GraphRuntime(null, graphData); + + Assert.AreEqual(rootCopy, graph.GetCopy(root)); + } + } + } +} diff --git a/Tests/Editor/GraphRuntimeTest.cs.meta b/Tests/Editor/GraphRuntimeTest.cs.meta new file mode 100644 index 0000000..0419a03 --- /dev/null +++ b/Tests/Editor/GraphRuntimeTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3f3d1ac0c1df42c6b16bfc59ed4ff44b +timeCreated: 1560878582 \ No newline at end of file diff --git a/Tests/Editor/Nodes.meta b/Tests/Editor/Nodes.meta new file mode 100644 index 0000000..df97390 --- /dev/null +++ b/Tests/Editor/Nodes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 28d313687cb942c180e520d641b34990 +timeCreated: 1561070005 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodeChoiceHubTest.cs b/Tests/Editor/Nodes/NodeChoiceHubTest.cs new file mode 100644 index 0000000..7a0f412 --- /dev/null +++ b/Tests/Editor/Nodes/NodeChoiceHubTest.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeChoiceHubTest { + private NodeChoiceHub _choiceHub; + private List _choiceList; + private List _conditions; + + [SetUp] + public void BeforeEach () { + _choiceList = new List(); + _conditions = new List(); + _choiceHub = new NodeChoiceHub(null, _choiceList, _conditions); + } + + public class IsValidProperty : NodeChoiceHubTest { + [Test] + public void It_should_return_true () { + Assert.IsTrue(_choiceHub.IsValid); + } + + [Test] + public void It_should_return_true_if_all_conditions_are_true () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(true); + _conditions.Add(condition); + + Assert.IsTrue(_choiceHub.IsValid); + } + + [Test] + public void It_should_return_false_if_any_conditions_are_false () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(false); + _conditions.Add(condition); + + Assert.IsFalse(_choiceHub.IsValid); + } + } + + public class HubChoicesProperty : NodeChoiceHubTest { + [Test] + public void It_should_return_choices_with_a_valid_child_node () { + var choice = Substitute.For(); + choice.IsValid.Returns(true); + choice.GetValidChildNode() + .Returns(x => A.Node.Build()); + _choiceList.Add(choice); + + Assert.IsTrue(_choiceHub.HubChoices.Contains(choice)); + } + + [Test] + public void It_should_not_return_choices_with_an_invalid_child_node () { + var choice = Substitute.For(); + choice.GetValidChildNode() + .Returns(x => null); + _choiceList.Add(choice); + + Assert.IsFalse(_choiceHub.HubChoices.Contains(choice)); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodeChoiceHubTest.cs.meta b/Tests/Editor/Nodes/NodeChoiceHubTest.cs.meta new file mode 100644 index 0000000..1af3424 --- /dev/null +++ b/Tests/Editor/Nodes/NodeChoiceHubTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dd68e7df1f8245268c30173597d9d944 +timeCreated: 1561143230 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodeDialogueTest.cs b/Tests/Editor/Nodes/NodeDialogueTest.cs new file mode 100644 index 0000000..d74fcbb --- /dev/null +++ b/Tests/Editor/Nodes/NodeDialogueTest.cs @@ -0,0 +1,316 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Actions; +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Choices; +using CleverCrow.Fluid.Dialogues.Conditions; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeDialogueTest { + private const string DIALOGUE = "Lorem Ipsum"; + private IActor _actor; + private List _children; + private List _choiceList; + private List _conditions; + private List _enterActions; + private List _exitActions; + + [SetUp] + public void BeforeEach () { + _actor = Substitute.For(); + _children = new List(); + _choiceList = new List(); + _conditions = new List(); + _enterActions = new List(); + _exitActions = new List(); + } + + private NodeDialogue CreateNodeDialogue () { + var graphBuilder = A.Graph; + _children.ForEach(c => graphBuilder.WithNode(c)); + + return new NodeDialogue( + graphBuilder.Build(), + null, + _actor, + DIALOGUE, + null, + _children, + _choiceList, + _conditions, + _enterActions, + _exitActions); + } + + public class NextMethod : NodeDialogueTest { + [Test] + public void It_should_return_a_child_with_IsValid_true () { + var child = A.Node + .WithIsValid(true) + .Build(); + var childData = A.NodeData.WithNode(child).Build(); + _children.Add(childData); + var node = CreateNodeDialogue(); + + var result = node.Next(); + + Assert.AreEqual(child, result); + } + + [Test] + public void It_should_return_null_if_all_children_are_invalid () { + var child = A.Node + .WithIsValid(false) + .Build(); + var childData = A.NodeData.WithNode(child).Build(); + _children.Add(childData); + var node = CreateNodeDialogue(); + + var result = node.Next(); + + Assert.IsNull(result); + } + } + + public class PlayMethod { + public class SpeakEvents : NodeDialogueTest { + [Test] + public void It_should_trigger_a_speak_event () { + var child = A.Node + .WithIsValid(true) + .Build(); + var childData = A.NodeData.WithNode(child).Build(); + _children.Add(childData); + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.Speak.Received(1).Invoke(_actor, DIALOGUE); + } + } + + public class NodeEnterEvents : NodeDialogueTest { + [Test] + public void It_should_trigger_a_NodeEnter_event () { + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.NodeEnter.Received(1).Invoke(node); + } + } + + public class ChoiceEvents { + public class InternalChoices : NodeDialogueTest { + [Test] + public void It_should_trigger_a_choice_event_with_valid_choices () { + var choice = A.Choice.Build(); + _choiceList.Add(choice); + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.Choice.ReceivedWithAnyArgs(1).Invoke(_actor, DIALOGUE, _choiceList); + } + + [Test] + public void It_should_not_trigger_a_choice_and_speak_event () { + var choice = A.Choice.Build(); + _choiceList.Add(choice); + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.Speak.DidNotReceive().Invoke(_actor, DIALOGUE); + } + + [Test] + public void It_should_trigger_a_choice_event_with_a_choice_that_is_valid () { + var choice = A.Choice.Build(); + _choiceList.Add(choice); + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.Choice.ReceivedWithAnyArgs(1).Invoke(_actor, DIALOGUE, _choiceList); + } + + [Test] + public void It_should_not_trigger_a_choice_event_without_choices () { + var node = CreateNodeDialogue(); + var playback = Substitute.For(); + + node.Play(playback); + + playback.Events.Choice.DidNotReceive().Invoke(_actor, DIALOGUE, _choiceList); + } + } + + public class ChoiceHubs : NodeDialogueTest { + [Test] + public void It_should_use_choices_from_next_child_if_its_a_choice_hub () { + var playback = Substitute.For(); + var choice = A.Choice.Build(); + var choiceHub = A.Node + .WithHubChoice(choice) + .Build(); + var choiceHubData = A.NodeData.WithNode(choiceHub).Build(); + _children.Add(choiceHubData); + + var node = CreateNodeDialogue(); + node.Play(playback); + + playback.Events.Choice.Received(1).Invoke(_actor, DIALOGUE, choiceHub.HubChoices); + } + + [Test] + public void It_should_trigger_enter_node_on_a_used_choice_hub () { + var playback = Substitute.For(); + var choice = A.Choice.Build(); + var choiceHub = A.Node + .WithHubChoice(choice) + .Build(); + var choiceHubData = A.NodeData.WithNode(choiceHub).Build(); + _children.Add(choiceHubData); + + var node = CreateNodeDialogue(); + node.Play(playback); + + playback.Events.NodeEnter.Received(1).Invoke(choiceHub); + } + + [Test] + public void It_should_not_use_a_choice_hub_if_choices_are_present () { + var playback = Substitute.For(); + var choice = A.Choice.Build(); + var choiceHub = A.Node + .WithHubChoice(choice) + .Build(); + var choiceHubData = A.NodeData.WithNode(choiceHub).Build(); + _children.Add(choiceHubData); + + _choiceList.Add(A.Choice.Build()); + var node = CreateNodeDialogue(); + node.Play(playback); + + playback.Events.Choice.DidNotReceive().Invoke(_actor, DIALOGUE, choiceHub.HubChoices); + } + + [Test] + public void It_should_not_use_a_choice_hub_if_its_invalid () { + var playback = Substitute.For(); + var choice = A.Choice.Build(); + var choiceHub = A.Node + .WithHubChoice(choice) + .WithIsValid(false) + .Build(); + var choiceHubData = A.NodeData.WithNode(choiceHub).Build(); + _children.Add(choiceHubData); + + var node = CreateNodeDialogue(); + node.Play(playback); + + playback.Events.Choice.DidNotReceive().Invoke(_actor, DIALOGUE, choiceHub.HubChoices); + } + + [Test] + public void It_should_not_error_if_choices_are_null () { + var playback = Substitute.For(); + var choiceHub = A.Node.Build(); + List choices = null; + choiceHub.HubChoices.Returns(choices); + var choiceHubData = A.NodeData.WithNode(choiceHub).Build(); + _children.Add(choiceHubData); + + var node = CreateNodeDialogue(); + node.Play(playback); + + Assert.DoesNotThrow(() => node.Play(playback)); + } + } + } + } + + public class IsValid : NodeDialogueTest { + [Test] + public void It_should_return_true_if_all_conditions_are_true () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(true); + _conditions.Add(condition); + var node = CreateNodeDialogue(); + + Assert.IsTrue(node.IsValid); + } + + [Test] + public void It_should_return_false_if_any_conditions_are_false () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(false); + _conditions.Add(condition); + var node = CreateNodeDialogue(); + + Assert.IsFalse(node.IsValid); + } + } + + public class EnterActionsProperty : NodeDialogueTest { + [Test] + public void It_should_be_populated_by_the_constructor () { + var action = Substitute.For(); + _enterActions.Add(action); + + var node = CreateNodeDialogue(); + + Assert.IsTrue(node.EnterActions.Contains(action)); + } + } + + public class ExitActionsProperty : NodeDialogueTest { + [Test] + public void It_should_be_populated_by_the_constructor () { + var action = Substitute.For(); + _exitActions.Add(action); + + var node = CreateNodeDialogue(); + + Assert.IsTrue(node.ExitActions.Contains(action)); + } + } + + public class GetChoiceMethod : NodeDialogueTest { + [Test] + public void It_should_return_valid_choices_emitted_by_Play () { + var playback = Substitute.For(); + var choice = A.Choice.Build(); + _choiceList.Add(choice); + + var node = CreateNodeDialogue(); + node.Play(playback); + var result = node.GetChoice(0); + + Assert.AreEqual(choice, result); + } + + [Test] + public void It_should_not_return_invalid_choices_emitted_by_Play () { + var playback = Substitute.For(); + var choice = A.Choice + .WithIsValid(false) + .Build(); + _choiceList.Add(choice); + + var node = CreateNodeDialogue(); + node.Play(playback); + var result = node.GetChoice(0); + + Assert.IsNull(result); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodeDialogueTest.cs.meta b/Tests/Editor/Nodes/NodeDialogueTest.cs.meta new file mode 100644 index 0000000..a5e2af2 --- /dev/null +++ b/Tests/Editor/Nodes/NodeDialogueTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b4c09b0eb4d4412ca3ed77d8de64ce72 +timeCreated: 1560805453 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodeHubTest.cs b/Tests/Editor/Nodes/NodeHubTest.cs new file mode 100644 index 0000000..c390e81 --- /dev/null +++ b/Tests/Editor/Nodes/NodeHubTest.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Conditions; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeHubTest { + private List _children; + private List _conditions; + + [SetUp] + public void BeforeEach () { + _conditions = new List(); + _children = new List(); + } + + private NodeHub CreateNodeHub () { + var graphBuilder = A.Graph; + _children.ForEach(c => graphBuilder.WithNode(c)); + + return new NodeHub(graphBuilder.Build(), null, _children, _conditions, null, null); + } + + public class NextMethod : NodeHubTest { + [Test] + public void It_should_return_a_child_with_IsValid_true () { + var child = A.Node + .WithIsValid(true) + .Build(); + var childData = A.NodeData.WithNode(child).Build(); + _children.Add(childData); + var hub = CreateNodeHub(); + + var result = hub.Next(); + + Assert.AreEqual(child, result); + } + + [Test] + public void It_should_return_null_if_all_children_are_invalid () { + var child = A.Node + .WithIsValid(false) + .Build(); + var childData = A.NodeData.WithNode(child).Build(); + _children.Add(childData); + var hub = CreateNodeHub(); + + var result = hub.Next(); + + Assert.IsNull(result); + } + } + + public class PlayMethod : NodeHubTest { + [Test] + public void It_should_trigger_next_on_playback () { + var playback = Substitute.For(); + + var hub = CreateNodeHub(); + hub.Play(playback); + + playback.Received(1).Next(); + } + } + + public class IsValidProperty : NodeHubTest { + [Test] + public void It_should_return_true_if_all_conditions_are_true () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(true); + _conditions.Add(condition); + var hub = CreateNodeHub(); + + Assert.IsTrue(hub.IsValid); + } + + [Test] + public void It_should_return_false_if_all_conditions_are_false () { + var condition = Substitute.For(); + condition.GetIsValid(Arg.Any()).Returns(false); + _conditions.Add(condition); + var hub = CreateNodeHub(); + + Assert.IsFalse(hub.IsValid); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodeHubTest.cs.meta b/Tests/Editor/Nodes/NodeHubTest.cs.meta new file mode 100644 index 0000000..6ba3d47 --- /dev/null +++ b/Tests/Editor/Nodes/NodeHubTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2510f4a9859453fb16bca960bd78d6a +timeCreated: 1561140970 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodeLinkTest.cs b/Tests/Editor/Nodes/NodeLinkTest.cs new file mode 100644 index 0000000..3ba2cdc --- /dev/null +++ b/Tests/Editor/Nodes/NodeLinkTest.cs @@ -0,0 +1,66 @@ +using CleverCrow.Fluid.Dialogues.Builders; +using CleverCrow.Fluid.Dialogues.Graphs; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeLinkTest { + private INode _child; + private NodeLink _link; + private IGraph _graph; + + [SetUp] + public void BeforeEach () { + _child = A.Node.Build(); + var childData = A.NodeData.WithNode(_child).Build(); + _graph = A.Graph.WithNode(childData).Build(); + _link = new NodeLink(_graph, null, childData, null, null, null); + } + + public class IsValidProperty : NodeLinkTest { + [Test] + public void It_should_return_the_child_IsValid_status () { + _child.IsValid.Returns(true); + + Assert.IsTrue(_link.IsValid); + } + + [Test] + public void It_should_not_crash_if_there_is_no_child () { + _link = new NodeLink(_graph, null, null, null, null, null); + + Assert.DoesNotThrow(() => { + var value = _link.IsValid; + Assert.IsFalse(value); + }); + } + } + + public class NextMethod : NodeLinkTest { + [Test] + public void It_should_return_its_child () { + _child.IsValid.Returns(true); + + Assert.AreEqual(_child, _link.Next()); + } + + [Test] + public void It_should_not_return_its_child_if_invalid () { + _child.IsValid.Returns(false); + + Assert.IsNull(_link.Next()); + } + } + + public class PlayMethod : NodeLinkTest { + [Test] + public void It_should_trigger_next_on_playback () { + var playback = Substitute.For(); + + _link.Play(playback); + + playback.Received(1).Next(); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodeLinkTest.cs.meta b/Tests/Editor/Nodes/NodeLinkTest.cs.meta new file mode 100644 index 0000000..023ef52 --- /dev/null +++ b/Tests/Editor/Nodes/NodeLinkTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 90a52cc2c734413eb2d5452c27864005 +timeCreated: 1561135234 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodePlayGraphTest.cs b/Tests/Editor/Nodes/NodePlayGraphTest.cs new file mode 100644 index 0000000..332d4cb --- /dev/null +++ b/Tests/Editor/Nodes/NodePlayGraphTest.cs @@ -0,0 +1,21 @@ +using CleverCrow.Fluid.Dialogues.Graphs; +using CleverCrow.Fluid.Dialogues.Nodes.PlayGraph; +using NSubstitute; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodePlayGraphTest { + public class PlayMethod { + [Test] + public void It_should_call_PlayChild_on_the_DialogueController () { + var graph = Substitute.For(); + var playGraph = new NodePlayGraph(null, null, graph, null, null, null, null); + var playback = Substitute.For(); + + playGraph.Play(playback); + + playback.ParentCtrl.Received(1).PlayChild(graph); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodePlayGraphTest.cs.meta b/Tests/Editor/Nodes/NodePlayGraphTest.cs.meta new file mode 100644 index 0000000..622e102 --- /dev/null +++ b/Tests/Editor/Nodes/NodePlayGraphTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 93d4443b147e410490fea2c0bff2ce1b +timeCreated: 1561153376 \ No newline at end of file diff --git a/Tests/Editor/Nodes/NodeRootTest.cs b/Tests/Editor/Nodes/NodeRootTest.cs new file mode 100644 index 0000000..ca48f93 --- /dev/null +++ b/Tests/Editor/Nodes/NodeRootTest.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using CleverCrow.Fluid.Dialogues.Builders; +using NUnit.Framework; + +namespace CleverCrow.Fluid.Dialogues.Nodes { + public class NodeRootTest { + public class NextMethod { + [Test] + public void It_should_return_a_valid_child () { + var node = A.Node.Build(); + var nodeData = A.NodeData.WithNode(node).Build(); + var children = new List { nodeData }; + var graph = A.Graph.WithNode(nodeData).Build(); + + var root = new NodeRoot(graph, null, children, null, null, null); + + Assert.AreEqual(node, root.Next()); + } + + [Test] + public void It_should_not_return_an_invalid_child () { + var child = A.Node.WithIsValid(false).Build(); + var childData = A.NodeData.WithNode(child).Build(); + var children = new List { childData }; + var graph = A.Graph.WithNode(childData).Build(); + + var root = new NodeRoot(graph, null, children, null, null, null); + + Assert.AreEqual(null, root.Next()); + } + } + } +} diff --git a/Tests/Editor/Nodes/NodeRootTest.cs.meta b/Tests/Editor/Nodes/NodeRootTest.cs.meta new file mode 100644 index 0000000..45e78a7 --- /dev/null +++ b/Tests/Editor/Nodes/NodeRootTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c4018fa99e504492bb3d27604ec03ec4 +timeCreated: 1561070042 \ No newline at end of file diff --git a/Tests/Editor/Utilities.meta b/Tests/Editor/Utilities.meta new file mode 100644 index 0000000..78129b2 --- /dev/null +++ b/Tests/Editor/Utilities.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 786876dd440e4c4e8492c9b69cb9e02c +timeCreated: 1563655736 \ No newline at end of file diff --git a/Tests/Editor/Utilities/RectCleanerTest.cs b/Tests/Editor/Utilities/RectCleanerTest.cs new file mode 100644 index 0000000..93db879 --- /dev/null +++ b/Tests/Editor/Utilities/RectCleanerTest.cs @@ -0,0 +1,52 @@ +using CleverCrow.Fluid.Dialogues.Editors; +using NUnit.Framework; +using UnityEngine; + +namespace FluidDialogue.Tests.Editor.Utilities { + public class RectCleanerTest { + public class Positions { + [Test] + public void It_should_do_nothing_to_a_normal_rect_position () { + var rect = new Rect(0, 0, 10, 10); + + var result = rect.FixNegativeSize(); + + Assert.AreEqual(Vector2.zero, result.position); + } + + [Test] + public void It_should_adjust_axis_with_negative_size () { + var rect = new Rect(0, 0, -10, -10); + var expected = new Vector2(rect.x + rect.width, rect.y + rect.height); + + var result = rect.FixNegativeSize(); + + Assert.AreEqual(expected, result.position); + } + } + + public class Sizes { + [Test] + public void It_should_do_nothing_to_a_normal_rect_size () { + var expectedSize = new Vector2(10, 10); + var rect = new Rect(Vector2.zero, expectedSize); + + var result = rect.FixNegativeSize(); + + Assert.AreEqual(expectedSize, result.size); + } + + [Test] + public void It_should_reverse_negative_size () { + var rect = new Rect(0, 0, -10, -10); + var expectedSize = new Vector2( + Mathf.Abs(rect.size.x), + Mathf.Abs(rect.size.y)); + + var result = rect.FixNegativeSize(); + + Assert.AreEqual(expectedSize, result.size); + } + } + } +} diff --git a/Tests/Editor/Utilities/RectCleanerTest.cs.meta b/Tests/Editor/Utilities/RectCleanerTest.cs.meta new file mode 100644 index 0000000..670942f --- /dev/null +++ b/Tests/Editor/Utilities/RectCleanerTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c78a71b4453d46e4bf498b3122d094db +timeCreated: 1563655746 \ No newline at end of file diff --git a/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef b/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef new file mode 100644 index 0000000..bdb0c3d --- /dev/null +++ b/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef @@ -0,0 +1,26 @@ +{ + "name": "com.fluid.dialogue.Editor.Tests", + "references": [ + "com.fluid.dialogue", + "com.fluid.dialogue.Editor", + "com.fluid.database", + "com.fluid.unity-event-plus", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "NSubstitute.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [] +} \ No newline at end of file diff --git a/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef.meta b/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef.meta new file mode 100644 index 0000000..2cb0ab5 --- /dev/null +++ b/Tests/Editor/com.fluid.dialogue.Editor.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1cdefd57abfe06e499e1e6d2512982e6 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json new file mode 100644 index 0000000..3a572fe --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "com.fluid.dialogue", + "version": "0.0.0", + "displayName": "Fluid Dialogue", + "description": "A Unity dialogue system that features an easy to use drag and drop graph. ScriptableObject driven with the ability to write custom actions and conditions to create complex dialogue workflows.", + "unity": "2019.1", + "dependencies": { + "com.fluid.database": "2.1.0", + "com.fluid.unity-event-plus": "1.2.0", + "com.fluid.simple-spellcheck": "1.1.1", + "com.fluid.find-and-replace": "1.0.1" + } +} diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..fbc36f1 --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4d19c1cc336223c4ab5b6a7b5839c91a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: