diff --git a/Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset b/Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset index 979130d9..93de3834 100644 --- a/Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset +++ b/Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset @@ -19,3 +19,4 @@ MonoBehaviour: - {fileID: 552225285624929822, guid: e39992796d5459442be9967c77e27066, type: 3} - {fileID: 7644433920135100480, guid: 12d242e44fe80ab44af852254b7cab0f, type: 3} - {fileID: 1794231825201849485, guid: 6fb0d7fc6faad154b8c3e3cb7abb7c15, type: 3} + - {fileID: 6399527186463168339, guid: ac41583865245bc4bb3de6c15929b9fc, type: 3} diff --git a/Assets/Data/Items/ChocoBirdPuzzleItems/Chocolate.asset b/Assets/Data/Items/ChocoBirdPuzzleItems/Chocolate.asset index 39d82fba..d661d1f9 100644 --- a/Assets/Data/Items/ChocoBirdPuzzleItems/Chocolate.asset +++ b/Assets/Data/Items/ChocoBirdPuzzleItems/Chocolate.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Chocolate m_EditorClassIdentifier: + _itemId: chocolate_4f2011c6 itemName: Chocolate description: sweet nectar of gods that lures the beasts from bushes mapSprite: {fileID: -509776585262497855, guid: c648336c825f7d7479582bbe4d95d0bc, type: 3} diff --git a/Assets/Data/Items/ChocoBirdPuzzleItems/LureSpotB.asset b/Assets/Data/Items/ChocoBirdPuzzleItems/LureSpotB.asset index 658e23f6..258e194c 100644 --- a/Assets/Data/Items/ChocoBirdPuzzleItems/LureSpotB.asset +++ b/Assets/Data/Items/ChocoBirdPuzzleItems/LureSpotB.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: LureSpotB m_EditorClassIdentifier: + _itemId: lurespotb_92c24cf9 itemName: LureSpotB description: Place where we atract the chocobird mapSprite: {fileID: 2640071299338251634, guid: 7c4c9881382767047aac78ca51b82923, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Ass.asset b/Assets/Data/Items/ExampleAss/Ass.asset index bacd3080..d74d35e6 100644 --- a/Assets/Data/Items/ExampleAss/Ass.asset +++ b/Assets/Data/Items/ExampleAss/Ass.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Ass m_EditorClassIdentifier: + _itemId: ass_41f5d715 itemName: TestAss description: Well... how do you like 'em? mapSprite: {fileID: -5471482676633547895, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Ass1.asset b/Assets/Data/Items/ExampleAss/Ass1.asset index 0b653942..0c4083d8 100644 --- a/Assets/Data/Items/ExampleAss/Ass1.asset +++ b/Assets/Data/Items/ExampleAss/Ass1.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Ass1 m_EditorClassIdentifier: + _itemId: ass1_5469eaae itemName: TestAss1 description: Well... how do you like 'em? mapSprite: {fileID: -5471482676633547895, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Ass2.asset b/Assets/Data/Items/ExampleAss/Ass2.asset index eb94e2df..0b9a5a6b 100644 --- a/Assets/Data/Items/ExampleAss/Ass2.asset +++ b/Assets/Data/Items/ExampleAss/Ass2.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Ass2 m_EditorClassIdentifier: + _itemId: ass2_c4e69d34 itemName: TestAss2 description: Well... how do you like 'em? mapSprite: {fileID: 4013237533433454447, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Ass3.asset b/Assets/Data/Items/ExampleAss/Ass3.asset index cb343013..3f7483b8 100644 --- a/Assets/Data/Items/ExampleAss/Ass3.asset +++ b/Assets/Data/Items/ExampleAss/Ass3.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Ass3 m_EditorClassIdentifier: + _itemId: ass3_cb8f9cfb itemName: TestAss3 description: Well... how do you like 'em? mapSprite: {fileID: -8109988653212156562, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Axe.asset b/Assets/Data/Items/ExampleAss/Axe.asset index 550d79ed..e05fc931 100644 --- a/Assets/Data/Items/ExampleAss/Axe.asset +++ b/Assets/Data/Items/ExampleAss/Axe.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Axe m_EditorClassIdentifier: + _itemId: axe_bf031e9e itemName: Axe description: Well... how do you like 'em? mapSprite: {fileID: 6674386295937086461, guid: 3bd1c178a78fcd144965cd1731dc309b, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Bonfire.asset b/Assets/Data/Items/ExampleAss/Bonfire.asset index 58b3565c..b6217253 100644 --- a/Assets/Data/Items/ExampleAss/Bonfire.asset +++ b/Assets/Data/Items/ExampleAss/Bonfire.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Bonfire m_EditorClassIdentifier: + _itemId: bonfire_818563eb itemName: Bonfire description: Well... how do you like 'em? mapSprite: {fileID: 6277786745957211492, guid: 016288f704d38e747a8eac9795051717, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Head.asset b/Assets/Data/Items/ExampleAss/Head.asset index 4d86d7ee..1a282ca5 100644 --- a/Assets/Data/Items/ExampleAss/Head.asset +++ b/Assets/Data/Items/ExampleAss/Head.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Head m_EditorClassIdentifier: + _itemId: head_c827d69b itemName: Axe description: Well... how do you like 'em? mapSprite: {fileID: -1693247529382600072, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} diff --git a/Assets/Data/Items/ExampleAss/Meat.asset b/Assets/Data/Items/ExampleAss/Meat.asset index 8818e3d4..1b082b82 100644 --- a/Assets/Data/Items/ExampleAss/Meat.asset +++ b/Assets/Data/Items/ExampleAss/Meat.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Meat m_EditorClassIdentifier: + _itemId: meat_6500cc13 itemName: Meat description: Well... how do you like 'em? mapSprite: {fileID: 6282751622250221668, guid: 204325ac88be74d4d882a078c64cf5e1, type: 3} diff --git a/Assets/Data/Items/ExampleAss/PickupItemData.asset b/Assets/Data/Items/ExampleAss/PickupItemData.asset new file mode 100644 index 00000000..06554903 --- /dev/null +++ b/Assets/Data/Items/ExampleAss/PickupItemData.asset @@ -0,0 +1,18 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} + m_Name: PickupItemData + m_EditorClassIdentifier: AppleHillsScripts::PickupItemData + _itemId: pickupitemdata_10de237f + itemName: + description: + mapSprite: {fileID: 0} diff --git a/Assets/Data/Items/ExampleAss/PickupItemData.asset.meta b/Assets/Data/Items/ExampleAss/PickupItemData.asset.meta new file mode 100644 index 00000000..90a59e77 --- /dev/null +++ b/Assets/Data/Items/ExampleAss/PickupItemData.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1cd8267212dd2349ad7e2b91ec5be11 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Data/Items/FootballBirdPuzzleItems/FootballBall.asset b/Assets/Data/Items/FootballBirdPuzzleItems/FootballBall.asset index 18f5608e..cff7ef08 100644 --- a/Assets/Data/Items/FootballBirdPuzzleItems/FootballBall.asset +++ b/Assets/Data/Items/FootballBirdPuzzleItems/FootballBall.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: FootballBall m_EditorClassIdentifier: + _itemId: footballball_b37968c2 itemName: FootballBall description: Ball that is used to attract the football bird. mapSprite: {fileID: -6720685004267932717, guid: 88acd207b6a9e114ca3d3e59ac3aa550, type: 3} diff --git a/Assets/Data/Items/FootballBirdPuzzleItems/LureSpotA.asset b/Assets/Data/Items/FootballBirdPuzzleItems/LureSpotA.asset index add9f257..1b345199 100644 --- a/Assets/Data/Items/FootballBirdPuzzleItems/LureSpotA.asset +++ b/Assets/Data/Items/FootballBirdPuzzleItems/LureSpotA.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: LureSpotA m_EditorClassIdentifier: + _itemId: lurespota_19a6cf7c itemName: LureSpotA description: Place where we atract the footballbird mapSprite: {fileID: -8438005379329254897, guid: fe735eb4f8856904caec179520dcb92f, type: 3} diff --git a/Assets/Data/Items/HammerBirdPuzzleItems/LureSpotC.asset b/Assets/Data/Items/HammerBirdPuzzleItems/LureSpotC.asset index 1d0f93e4..837a9602 100644 --- a/Assets/Data/Items/HammerBirdPuzzleItems/LureSpotC.asset +++ b/Assets/Data/Items/HammerBirdPuzzleItems/LureSpotC.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: LureSpotC m_EditorClassIdentifier: + _itemId: lurespotc_559f54b7 itemName: LureSpotC description: Place where we atract the hammerbird mapSprite: {fileID: 3009302558630513478, guid: 3aefb78ee1b7f6e4685d0fff00e24e3d, type: 3} diff --git a/Assets/Data/Items/HammerBirdPuzzleItems/Nails.asset b/Assets/Data/Items/HammerBirdPuzzleItems/Nails.asset index cbd258c8..0e834070 100644 --- a/Assets/Data/Items/HammerBirdPuzzleItems/Nails.asset +++ b/Assets/Data/Items/HammerBirdPuzzleItems/Nails.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Nails m_EditorClassIdentifier: + _itemId: nails_2ecaef33 itemName: Nails description: Nails used to lure the hammer bird out. mapSprite: {fileID: 21300000, guid: 34b9ddb77aae2df4ab489b7bb8f16cff, type: 3} diff --git a/Assets/Data/Items/SoundBirdPuzzleItems/Bunfflers.asset b/Assets/Data/Items/SoundBirdPuzzleItems/Bunfflers.asset index efd7f6af..792f85a6 100644 --- a/Assets/Data/Items/SoundBirdPuzzleItems/Bunfflers.asset +++ b/Assets/Data/Items/SoundBirdPuzzleItems/Bunfflers.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Bunfflers m_EditorClassIdentifier: + _itemId: bunfflers_07f2a135 itemName: Bunfflers description: Ear Mufflers made to put on the soundbird so it doesn't fly away. mapSprite: {fileID: -8848419083337551572, guid: b285cfb6530624a44a95ec99a59d7215, type: 3} diff --git a/Assets/Data/Items/SoundBirdPuzzleItems/BurgerBuns.asset b/Assets/Data/Items/SoundBirdPuzzleItems/BurgerBuns.asset index 6b842cc2..9a1372c3 100644 --- a/Assets/Data/Items/SoundBirdPuzzleItems/BurgerBuns.asset +++ b/Assets/Data/Items/SoundBirdPuzzleItems/BurgerBuns.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: BurgerBuns m_EditorClassIdentifier: + _itemId: burgerbuns_4ccd8772 itemName: BurgerBuns description: A pack of Burguer Buns, they need to be combined with the headband to get "Bunfflers" diff --git a/Assets/Data/Items/SoundBirdPuzzleItems/Headband.asset b/Assets/Data/Items/SoundBirdPuzzleItems/Headband.asset index 6e53aa1f..9750a53b 100644 --- a/Assets/Data/Items/SoundBirdPuzzleItems/Headband.asset +++ b/Assets/Data/Items/SoundBirdPuzzleItems/Headband.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: Headband m_EditorClassIdentifier: + _itemId: headband_9a56f411 itemName: Headband description: Item dropped by Uncle Kunt! mapSprite: {fileID: 743298078511449570, guid: 6f463983177b1404ca39fa222f03a3b2, type: 3} diff --git a/Assets/Data/Items/SoundBirdPuzzleItems/SoundBird.asset b/Assets/Data/Items/SoundBirdPuzzleItems/SoundBird.asset index 1318d8ce..382fe57d 100644 --- a/Assets/Data/Items/SoundBirdPuzzleItems/SoundBird.asset +++ b/Assets/Data/Items/SoundBirdPuzzleItems/SoundBird.asset @@ -12,6 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 951b5c8af5114086a865d4bb7eae4548, type: 3} m_Name: SoundBird m_EditorClassIdentifier: + _itemId: soundbird_16b17ba9 itemName: SoundBird description: Objective Bird, escapes if player makes sound when close to it, stays quiet if earmufflers are applied. diff --git a/Assets/Dialogue/TestDialogue.dialoguegraph b/Assets/Dialogue/TestDialogue.dialoguegraph index ad2a248a..04e171cd 100644 --- a/Assets/Dialogue/TestDialogue.dialoguegraph +++ b/Assets/Dialogue/TestDialogue.dialoguegraph @@ -36,9 +36,6 @@ MonoBehaviour: - rid: 1219994508087787764 - rid: 1219994508087787770 - rid: 1219994508087787776 - - rid: 1219994508087787907 - - rid: 1219994508087787922 - - rid: 1219994508087787946 m_GraphWireModels: - rid: 1219994508087787755 - rid: 1219994508087787773 @@ -58,7 +55,7 @@ MonoBehaviour: x: 123 y: -65 width: 1698 - height: 763 + height: 244 m_GraphElementMetaData: - m_Guid: m_Value0: 13346176596883742728 @@ -156,30 +153,6 @@ MonoBehaviour: Hash: 1b76d4d1d2059b91f866d271199acbfd m_Category: 2 m_Index: 4 - - m_Guid: - m_Value0: 17687616821000604665 - m_Value1: 11418671301582040963 - m_HashGuid: - serializedVersion: 2 - Hash: f90bf722c90977f583afbed10c45779e - m_Category: 0 - m_Index: 6 - - m_Guid: - m_Value0: 5830891262905902794 - m_Value1: 8531471494669057217 - m_HashGuid: - serializedVersion: 2 - Hash: ca666fd1b57ceb50c1bc1e6b1fe06576 - m_Category: 0 - m_Index: 7 - - m_Guid: - m_Value0: 16523742220227669027 - m_Value1: 12072672733572090161 - m_HashGuid: - serializedVersion: 2 - Hash: 236ccbb4002050e531c91b2ee3bf8aa7 - m_Category: 0 - m_Index: 8 m_EntryPoint: rid: 1219994508087787747 m_Graph: @@ -431,7 +404,7 @@ MonoBehaviour: serializedVersion: 2 Hash: 3cbfd8ef332fa01b78a5ecc384f2e5e7 m_Version: 2 - m_Position: {x: 905.0175, y: -60.677498} + m_Position: {x: 914.5827, y: -58.93837} m_Title: DialogueNode m_Tooltip: m_NodePreviewModel: @@ -691,23 +664,23 @@ MonoBehaviour: - rid: 1219994508087787897 type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: - m_Value: + m_Value: This is a test line - rid: 1219994508087787898 type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: - m_Value: + m_Value: Another regular line - rid: 1219994508087787900 type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: - m_Value: + m_Value: One multiline - rid: 1219994508087787901 type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: - m_Value: + m_Value: Two multiline - rid: 1219994508087787902 type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: - m_Value: + m_Value: Three multiline - rid: 1219994508087787904 type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: @@ -720,287 +693,3 @@ MonoBehaviour: type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} data: m_Value: 0 - - rid: 1219994508087787907 - type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} - data: - m_Guid: - m_Value0: 17687616821000604665 - m_Value1: 11418671301582040963 - m_HashGuid: - serializedVersion: 2 - Hash: f90bf722c90977f583afbed10c45779e - m_Version: 2 - m_Position: {x: 337.83755, y: 301.66} - m_Title: - m_Tooltip: - m_NodePreviewModel: - rid: -2 - m_State: 0 - m_InputConstantsById: - m_KeyList: - - __option_DialogueLineType - - __option_NoLines - - DefaultDialogueLine - - LoopThroughDefaultLines - - RequiredPickup - m_ValueList: - - rid: 1219994508087787908 - - rid: 1219994508087787909 - - rid: 1219994508087787910 - - rid: 1219994508087787911 - - rid: 1219994508087787916 - m_InputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_OutputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_Collapsed: 0 - m_CurrentModeIndex: 0 - m_ElementColor: - m_Color: {r: 0, g: 0, b: 0, a: 0} - m_HasUserColor: 0 - m_Node: - rid: 1219994508087787913 - - rid: 1219994508087787908 - type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - m_Value: 0 - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - - rid: 1219994508087787909 - type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 1 - - rid: 1219994508087787910 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - - rid: 1219994508087787911 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 0 - - rid: 1219994508087787913 - type: {class: WaitOnPickup, ns: Editor.Dialogue, asm: AppleHillsEditor} - data: - - rid: 1219994508087787916 - type: {class: 'Constant`1[[PickupItemData, AppleHillsScripts]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: {fileID: 0} - - rid: 1219994508087787922 - type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} - data: - m_Guid: - m_Value0: 5830891262905902794 - m_Value1: 8531471494669057217 - m_HashGuid: - serializedVersion: 2 - Hash: ca666fd1b57ceb50c1bc1e6b1fe06576 - m_Version: 2 - m_Position: {x: 806, y: 304} - m_Title: - m_Tooltip: - m_NodePreviewModel: - rid: -2 - m_State: 0 - m_InputConstantsById: - m_KeyList: - - __option_DialogueLineType - - __option_NoLines - - __option_IncorrectItemDialogueLineType - - __option_IncorrectItemNoLines - - __option_ForbiddenItemDialogueLineType - - __option_ForbiddenItemNoLines - - DefaultDialogueLine - - LoopThroughDefaultLines - - RequiredSlot - - LoopThroughIncorrectItemLines - - ForbiddenItemDialogueLine - - LoopThroughForbiddenItemLines - - IncorrectItemDialogueLine1 - m_ValueList: - - rid: 1219994508087787923 - - rid: 1219994508087787924 - - rid: 1219994508087787925 - - rid: 1219994508087787926 - - rid: 1219994508087787927 - - rid: 1219994508087787928 - - rid: 1219994508087787929 - - rid: 1219994508087787930 - - rid: 1219994508087787931 - - rid: 1219994508087787933 - - rid: 1219994508087787934 - - rid: 1219994508087787935 - - rid: 1219994508087787938 - m_InputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_OutputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_Collapsed: 0 - m_CurrentModeIndex: 0 - m_ElementColor: - m_Color: {r: 0, g: 0, b: 0, a: 0} - m_HasUserColor: 0 - m_Node: - rid: 1219994508087787936 - - rid: 1219994508087787923 - type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - m_Value: 0 - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - - rid: 1219994508087787924 - type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 1 - - rid: 1219994508087787925 - type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - m_Value: 1 - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - - rid: 1219994508087787926 - type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 1 - - rid: 1219994508087787927 - type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - m_Value: 0 - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - - rid: 1219994508087787928 - type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 1 - - rid: 1219994508087787929 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - - rid: 1219994508087787930 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 0 - - rid: 1219994508087787931 - type: {class: 'Constant`1[[PickupItemData, AppleHillsScripts]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: {fileID: 0} - - rid: 1219994508087787933 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 0 - - rid: 1219994508087787934 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - - rid: 1219994508087787935 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 0 - - rid: 1219994508087787936 - type: {class: WaitOnSlot, ns: Editor.Dialogue, asm: AppleHillsEditor} - data: - - rid: 1219994508087787938 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - - rid: 1219994508087787946 - type: {class: UserNodeModelImp, ns: Unity.GraphToolkit.Editor.Implementation, asm: Unity.GraphToolkit.Editor} - data: - m_Guid: - m_Value0: 16523742220227669027 - m_Value1: 12072672733572090161 - m_HashGuid: - serializedVersion: 2 - Hash: 236ccbb4002050e531c91b2ee3bf8aa7 - m_Version: 2 - m_Position: {x: 1347.0507, y: 296.67874} - m_Title: - m_Tooltip: - m_NodePreviewModel: - rid: -2 - m_State: 0 - m_InputConstantsById: - m_KeyList: - - __option_DialogueLineType - - __option_NoLines - - RequiredPuzzleStep - - DefaultDialogueLine - - LoopThroughDefaultLines - m_ValueList: - - rid: 1219994508087787947 - - rid: 1219994508087787948 - - rid: 1219994508087787949 - - rid: 1219994508087787950 - - rid: 1219994508087787951 - m_InputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_OutputPortInfos: - expandedPortsById: - m_KeyList: [] - m_ValueList: - m_Collapsed: 0 - m_CurrentModeIndex: 0 - m_ElementColor: - m_Color: {r: 0, g: 0, b: 0, a: 0} - m_HasUserColor: 0 - m_Node: - rid: 1219994508087787952 - - rid: 1219994508087787947 - type: {class: EnumConstant, ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - m_Value: 0 - m_EnumType: - m_Identification: Editor.Dialogue.DialogueType, AppleHillsEditor, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - - rid: 1219994508087787948 - type: {class: 'Constant`1[[System.Int32, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 1 - - rid: 1219994508087787949 - type: {class: 'Constant`1[[PuzzleStepSO, AppleHillsScripts]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: {fileID: 0} - - rid: 1219994508087787950 - type: {class: 'Constant`1[[System.String, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: - - rid: 1219994508087787951 - type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: Unity.GraphToolkit.Internal.Editor} - data: - m_Value: 0 - - rid: 1219994508087787952 - type: {class: WaitOnPuzzleStep, ns: Editor.Dialogue, asm: AppleHillsEditor} - data: diff --git a/Assets/Editor/Dialogue/DialogueGraphImporter.cs b/Assets/Editor/Dialogue/DialogueGraphImporter.cs index b40bd920..3ca70124 100644 --- a/Assets/Editor/Dialogue/DialogueGraphImporter.cs +++ b/Assets/Editor/Dialogue/DialogueGraphImporter.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Dialogue; +using PuzzleS; namespace Editor.Dialogue { @@ -17,12 +18,13 @@ namespace Editor.Dialogue RuntimeDialogueGraph runtimeGraph = ScriptableObject.CreateInstance(); var nodeIDMap = new Dictionary(); + // Generate stable GUIDs for each node foreach (var node in editorGraph.GetNodes()) { nodeIDMap[node] = Guid.NewGuid().ToString(); } - // TODO: This could be done in the above loop, but for clarity, I'm keeping it separate for now. + // Process start node to get entry point and speaker name var startNode = editorGraph.GetNodes().OfType().FirstOrDefault(); if (startNode != null) { @@ -35,33 +37,193 @@ namespace Editor.Dialogue runtimeGraph.speakerName = GetPortValue(startNode.GetInputPortByName("SpeakerName")); } + // Process each node in the graph foreach (var iNode in editorGraph.GetNodes()) { - if (iNode is StartNode || iNode is EndNode) + if (iNode is StartNode || iNode is IVariableNode) continue; - var runtimeNode = new RuntimeDialogueNode{ nodeID = nodeIDMap[iNode]}; + var runtimeNode = new RuntimeDialogueNode{ nodeID = nodeIDMap[iNode] }; + + // Process node based on its type if (iNode is DialogueNode dialogueNode) { - ProcessDialogueNode(dialogueNode, runtimeNode, nodeIDMap); + if (iNode is WaitOnPuzzleStep puzzleNode) + { + ProcessPuzzleNode(puzzleNode, runtimeNode); + } + else if (iNode is WaitOnPickup pickupNode) + { + ProcessPickupNode(pickupNode, runtimeNode); + } + else if (iNode is WaitOnSlot slotNode) + { + ProcessSlotNode(slotNode, runtimeNode); + } + + // Process base dialogue node properties (for all node types) + ProcessDialogueNodeBase(dialogueNode, runtimeNode); + } + else if (iNode is EndNode) + { + runtimeNode.nodeType = RuntimeDialogueNodeType.End; + // End nodes don't have next nodes, so we don't need to look for "out" ports } + // Get next node connection (skip for EndNodes as they don't have out ports) + if (!(iNode is EndNode)) + { + // Check if the node has output ports before trying to get one by name + var outputPorts = iNode.GetOutputPorts(); + if (outputPorts != null && outputPorts.Any()) + { + var outPort = outputPorts.FirstOrDefault(p => p.name == "out"); + if (outPort != null && outPort.firstConnectedPort != null) + { + runtimeNode.nextNodeID = nodeIDMap[outPort.firstConnectedPort.GetNode()]; + } + } + } + + // Add node to runtime graph runtimeGraph.allNodes.Add(runtimeNode); } ctx.AddObjectToAsset("RuntimeData", runtimeGraph); ctx.SetMainObject(runtimeGraph); + + Debug.Log($"Imported DialogueGraph with {runtimeGraph.allNodes.Count} nodes."); } - private void ProcessDialogueNode(DialogueNode node, RuntimeDialogueNode runtimeNode, Dictionary nodeIDMap) + private void ProcessDialogueNodeBase(DialogueNode node, RuntimeDialogueNode runtimeNode) { - runtimeNode.dialogueLine = GetPortValue(node.GetInputPortByName("DialogueLine")); + // Set default node type + runtimeNode.nodeType = RuntimeDialogueNodeType.Dialogue; - var nextNodePort = node.GetOutputPortByName("out")?.firstConnectedPort; - if (nextNodePort != null) + // Get line type and count options + var lineTypeOption = node.GetNodeOptionByName("DialogueLineType"); + lineTypeOption.TryGetValue(out var lineType); + + var lineCountOption = node.GetNodeOptionByName("NoLines"); + lineCountOption.TryGetValue(out var lineCount); + + // Process dialogue lines based on line type + if (lineType == DialogueType.SayMultipleLines) { - runtimeNode.nextNodeID = nodeIDMap[nextNodePort.GetNode()]; + for (var i = 0; i < lineCount; i++) + { + var lineValue = GetPortValue(node.GetInputPortByName($"DefaultDialogueLine{i + 1}")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.dialogueLines.Add(lineValue); + } + } } + else + { + var lineValue = GetPortValue(node.GetInputPortByName("DefaultDialogueLine")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.dialogueLines.Add(lineValue); + } + } + + // Get loop through lines option + var loopThroughLines = GetPortValue(node.GetInputPortByName("LoopThroughDefaultLines")); + runtimeNode.loopThroughLines = loopThroughLines; + } + + private void ProcessPuzzleNode(WaitOnPuzzleStep node, RuntimeDialogueNode runtimeNode) + { + runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnPuzzleStep; + + var puzzleStep = GetPortValue(node.GetInputPortByName("RequiredPuzzleStep")); + if (puzzleStep != null) + { + runtimeNode.puzzleStepID = puzzleStep.stepId; + } + } + + private void ProcessPickupNode(WaitOnPickup node, RuntimeDialogueNode runtimeNode) + { + runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnPickup; + + var pickup = GetPortValue(node.GetInputPortByName("RequiredPickup")); + if (pickup != null) + { + runtimeNode.pickupItemID = pickup.itemId; + } + } + + private void ProcessSlotNode(WaitOnSlot node, RuntimeDialogueNode runtimeNode) + { + runtimeNode.nodeType = RuntimeDialogueNodeType.WaitOnSlot; + + var slot = GetPortValue(node.GetInputPortByName("RequiredSlot")); + if (slot != null) + { + runtimeNode.slotItemID = slot.itemId; + } + + // Process incorrect item lines + var incorrectItemLineTypeOption = node.GetNodeOptionByName("IncorrectItemDialogueLineType"); + incorrectItemLineTypeOption.TryGetValue(out var incorrectItemLineType); + + var incorrectItemLineCountOption = node.GetNodeOptionByName("IncorrectItemNoLines"); + incorrectItemLineCountOption.TryGetValue(out var incorrectItemLineCount); + + if (incorrectItemLineType == DialogueType.SayMultipleLines) + { + for (var i = 0; i < incorrectItemLineCount; i++) + { + var lineValue = GetPortValue(node.GetInputPortByName($"IncorrectItemDialogueLine{i + 1}")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.incorrectItemLines.Add(lineValue); + } + } + } + else + { + var lineValue = GetPortValue(node.GetInputPortByName("IncorrectItemDialogueLine")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.incorrectItemLines.Add(lineValue); + } + } + + runtimeNode.loopThroughIncorrectLines = + GetPortValue(node.GetInputPortByName("LoopThroughIncorrectItemLines")); + + // Process forbidden item lines + var forbiddenItemLineTypeOption = node.GetNodeOptionByName("ForbiddenItemDialogueLineType"); + forbiddenItemLineTypeOption.TryGetValue(out var forbiddenItemLineType); + + var forbiddenItemLineCountOption = node.GetNodeOptionByName("ForbiddenItemNoLines"); + forbiddenItemLineCountOption.TryGetValue(out var forbiddenItemLineCount); + + if (forbiddenItemLineType == DialogueType.SayMultipleLines) + { + for (var i = 0; i < forbiddenItemLineCount; i++) + { + var lineValue = GetPortValue(node.GetInputPortByName($"ForbiddenItemDialogueLine{i + 1}")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.forbiddenItemLines.Add(lineValue); + } + } + } + else + { + var lineValue = GetPortValue(node.GetInputPortByName("ForbiddenItemDialogueLine")); + if (!string.IsNullOrEmpty(lineValue)) + { + runtimeNode.forbiddenItemLines.Add(lineValue); + } + } + + runtimeNode.loopThroughForbiddenLines = + GetPortValue(node.GetInputPortByName("LoopThroughForbiddenItemLines")); } private T GetPortValue(IPort port) diff --git a/Assets/Prefabs/Managers/ItemManager.prefab b/Assets/Prefabs/Managers/ItemManager.prefab new file mode 100644 index 00000000..64851d69 --- /dev/null +++ b/Assets/Prefabs/Managers/ItemManager.prefab @@ -0,0 +1,46 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6399527186463168339 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7768516537241418043} + - component: {fileID: 4607144289768175859} + m_Layer: 0 + m_Name: ItemManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7768516537241418043 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6399527186463168339} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.64965, y: 3.24335, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &4607144289768175859 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6399527186463168339} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a78fe78378e6426da43710f6d0ae84ba, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::Core.ItemManager diff --git a/Assets/Prefabs/Managers/ItemManager.prefab.meta b/Assets/Prefabs/Managers/ItemManager.prefab.meta new file mode 100644 index 00000000..4bdb6ba5 --- /dev/null +++ b/Assets/Prefabs/Managers/ItemManager.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ac41583865245bc4bb3de6c15929b9fc +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Levels/AppleHillsOverworld.unity b/Assets/Scenes/Levels/AppleHillsOverworld.unity index bc82ec87..85ad2b0d 100644 --- a/Assets/Scenes/Levels/AppleHillsOverworld.unity +++ b/Assets/Scenes/Levels/AppleHillsOverworld.unity @@ -309,7 +309,7 @@ PrefabInstance: objectReference: {fileID: -8109988653212156562, guid: 77ab3d770c92d5344b36eee3293a0f94, type: 3} - target: {fileID: 6350287859698694726, guid: b5fc01af35233eb4cbeede05e50a7c34, type: 3} propertyPath: m_Name - value: TestAss + value: TestAss3 objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] @@ -368,6 +368,8 @@ SpriteRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -389,6 +391,7 @@ SpriteRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 @@ -995,7 +998,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 6350287859698694726, guid: b5fc01af35233eb4cbeede05e50a7c34, type: 3} propertyPath: m_Name - value: TestAss + value: TestAss2 objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] @@ -1228,6 +1231,8 @@ SpriteRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -1249,6 +1254,7 @@ SpriteRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: -1 @@ -1826,7 +1832,6 @@ MonoBehaviour: m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} m_RequiresDepthTexture: 0 m_RequiresColorTexture: 0 - m_Version: 2 m_TaaSettings: m_Quality: 3 m_FrameInfluence: 0.1 @@ -1834,6 +1839,7 @@ MonoBehaviour: m_MipBias: 0 m_VarianceClampScale: 0.9 m_ContrastAdaptiveSharpening: 0 + m_Version: 2 --- !u!1 &1880929593 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Core/ItemManager.cs b/Assets/Scripts/Core/ItemManager.cs new file mode 100644 index 00000000..2577f745 --- /dev/null +++ b/Assets/Scripts/Core/ItemManager.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using Interactions; + +namespace Core +{ + /// + /// Central registry for pickups and item slots. + /// Mirrors the singleton pattern used by PuzzleManager. + /// + public class ItemManager : MonoBehaviour + { + private static ItemManager _instance; + private static bool _isQuitting; + + public static ItemManager Instance + { + get + { + if (_instance == null && Application.isPlaying && !_isQuitting) + { + _instance = FindAnyObjectByType(); + if (_instance == null) + { + var go = new GameObject("ItemManager"); + _instance = go.AddComponent(); + // DontDestroyOnLoad(go); + } + } + return _instance; + } + } + + private readonly HashSet _pickups = new HashSet(); + private readonly HashSet _itemSlots = new HashSet(); + + // Central events forwarded from registered pickups/slots + // Broadcasts when any registered pickup was picked up (passes the picked item data) + public event Action OnItemPickedUp; + + // Broadcasts when any registered ItemSlot reports a correct item slotted + // Args: slot's itemData (the slot definition), then the slotted item data + public event Action OnCorrectItemSlotted; + + void Awake() + { + _instance = this; + } + + void Start() + { + // Subscribe to scene load completed so we can clear registrations when scenes change + // Access Instance directly to ensure the service is initialized and we get the event hookup. + SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted; + } + + void OnDestroy() + { + // Unsubscribe from SceneManagerService + if (SceneManagerService.Instance != null) + SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted; + + // Ensure we clean up any subscriptions from registered items when the manager is destroyed + ClearAllRegistrations(); + } + + void OnApplicationQuit() + { + _isQuitting = true; + } + + private void OnSceneLoadCompleted(string sceneName) + { + // Clear all registrations when a new scene is loaded, so no stale references persist + ClearAllRegistrations(); + } + + /// + /// Unsubscribe all pickup/slot event handlers and clear registries and manager events. + /// + private void ClearAllRegistrations() + { + // Unsubscribe pickup handlers + var pickupsCopy = new List(_pickups); + foreach (var p in pickupsCopy) + { + if (p != null) + p.OnItemPickedUp -= Pickup_OnItemPickedUp; + } + _pickups.Clear(); + + // Unsubscribe slot handlers + var slotsCopy = new List(_itemSlots); + foreach (var s in slotsCopy) + { + if (s != null) + s.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted; + } + _itemSlots.Clear(); + + // Clear manager-level event subscribers + OnItemPickedUp = null; + OnCorrectItemSlotted = null; + } + + public void RegisterPickup(Pickup pickup) + { + if (pickup == null) return; + // only subscribe if newly added to avoid duplicate subscriptions + if (_pickups.Add(pickup)) + { + pickup.OnItemPickedUp += Pickup_OnItemPickedUp; + } + } + + public void UnregisterPickup(Pickup pickup) + { + if (pickup == null) return; + if (_pickups.Remove(pickup)) + { + pickup.OnItemPickedUp -= Pickup_OnItemPickedUp; + } + } + + public void RegisterItemSlot(ItemSlot slot) + { + if (slot == null) return; + if (_itemSlots.Add(slot)) + { + slot.OnCorrectItemSlotted += ItemSlot_OnCorrectItemSlotted; + } + } + + public void UnregisterItemSlot(ItemSlot slot) + { + if (slot == null) return; + if (_itemSlots.Remove(slot)) + { + slot.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted; + } + } + + // Handler that forwards pickup events + private void Pickup_OnItemPickedUp(PickupItemData data) + { + OnItemPickedUp?.Invoke(data); + } + + // Handler that forwards correct-slot events + private void ItemSlot_OnCorrectItemSlotted(PickupItemData slotData, PickupItemData slottedItem) + { + OnCorrectItemSlotted?.Invoke(slotData, slottedItem); + } + + /// + /// Returns the current slot state for the given item data by searching registered slots. + /// If the item is currently slotted in a slot, returns that slot's state; otherwise returns ItemSlotState.None. + /// + public ItemSlotState GetSlotStatusForItem(PickupItemData itemData) + { + if (itemData == null) return ItemSlotState.None; + + foreach (var slot in _itemSlots) + { + var slottedObj = slot.GetSlottedObject(); + if (slottedObj == null) continue; + var pickup = slottedObj.GetComponent(); + if (pickup == null) continue; + if (pickup.itemData == itemData) + { + return slot.CurrentSlottedState; + } + } + return ItemSlotState.None; + } + + public IEnumerable Pickups => _pickups; + public IEnumerable ItemSlots => _itemSlots; + } +} diff --git a/Assets/Scripts/Core/ItemManager.cs.meta b/Assets/Scripts/Core/ItemManager.cs.meta new file mode 100644 index 00000000..c7dfbc70 --- /dev/null +++ b/Assets/Scripts/Core/ItemManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a78fe78378e6426da43710f6d0ae84ba +timeCreated: 1758888493 \ No newline at end of file diff --git a/Assets/Scripts/Dialogue/DialogueComponent.cs b/Assets/Scripts/Dialogue/DialogueComponent.cs index 7acda991..44aba83e 100644 --- a/Assets/Scripts/Dialogue/DialogueComponent.cs +++ b/Assets/Scripts/Dialogue/DialogueComponent.cs @@ -1,63 +1,466 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; +using Core; +using Interactions; using UnityEngine; -using UnityEngine.InputSystem; +using PuzzleS; namespace Dialogue { + [AddComponentMenu("Apple Hills/Dialogue/Dialogue Component")] public class DialogueComponent : MonoBehaviour { - [SerializeField] - private RuntimeDialogueGraph runtimeGraph; - private Dictionary _nodeLookup = new Dictionary(); - private RuntimeDialogueNode _currentNode; - - private void Start() + [SerializeField] private RuntimeDialogueGraph dialogueGraph; + + private RuntimeDialogueNode currentNode; + private int currentLineIndex; + private bool isWaitingForCondition; + + // Events + public event Action OnDialogueLineChanged; // speaker name, dialogue text + public event Action OnDialogueCompleted; + public event Action OnConditionStatusChanged; // Whether the condition is now met + + // References to managers + private ItemManager itemManager; + private PuzzleManager puzzleManager; + + // Properties + public bool IsActive { get; private set; } + public bool IsCompleted { get; private set; } + public string CurrentSpeakerName => dialogueGraph?.speakerName; + + private void Awake() { - foreach (var node in runtimeGraph.allNodes) - { - _nodeLookup[node.nodeID] = node; - } + // Auto-injection of managers + itemManager = FindFirstObjectByType(); + puzzleManager = FindFirstObjectByType(); - if(string.IsNullOrEmpty(runtimeGraph.entryNodeID)) + if (itemManager == null) + Debug.LogWarning("DialogueComponent: ItemManager not found in scene!"); + + if (puzzleManager == null) + Debug.LogWarning("DialogueComponent: PuzzleManager not found in scene!"); + } + + public void StartDialogue() + { + if (dialogueGraph == null) { - EndDialogue(); + Debug.LogError("DialogueComponent: No dialogue graph assigned!"); return; } - ShowNode(runtimeGraph.entryNodeID); + // Reset state + IsActive = true; + IsCompleted = false; + isWaitingForCondition = false; + currentLineIndex = 0; + + // Set to entry node + currentNode = dialogueGraph.GetNodeByID(dialogueGraph.entryNodeID); + + // Register for events based on current node + RegisterForEvents(); + + // Try to process the current node + ProcessCurrentNode(); } - - private void Update() + + public bool CanAdvance() { - if(Mouse.current.leftButton.wasPressedThisFrame && _currentNode != null) - { - if(string.IsNullOrEmpty(_currentNode.nextNodeID)) - { - EndDialogue(); - } - else - { - ShowNode(_currentNode.nextNodeID); - } - } + // Can't advance if dialogue is not active or is completed + if (!IsActive || IsCompleted) return false; + + // Check if we're waiting for a condition + if (isWaitingForCondition) return false; + + // Check if we have more lines in the current node + if (currentLineIndex < currentNode.dialogueLines.Count - 1 || + (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0)) + return true; + + // Check if we have a next node + return !string.IsNullOrEmpty(currentNode.nextNodeID); } - - private void ShowNode(string nodeID) + + public void Advance() { - if (!_nodeLookup.ContainsKey(nodeID)) + if (!CanAdvance()) return; + + // If we have more lines in the current node, advance to the next line + if (currentLineIndex < currentNode.dialogueLines.Count - 1) { - EndDialogue(); + currentLineIndex++; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); return; } - _currentNode = _nodeLookup[nodeID]; - Debug.Log($"{runtimeGraph.speakerName}: {_currentNode.dialogueLine}"); + // If we should loop through lines, reset the index + if (currentNode.loopThroughLines && currentNode.dialogueLines.Count > 0) + { + currentLineIndex = 0; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + return; + } + + // Otherwise, move to the next node + MoveToNextNode(); } - - private void EndDialogue() + + public void AdvanceToNextNode() { - Application.Quit(); + if (!IsActive || IsCompleted) return; + + // Force move to the next node, regardless of current line index + MoveToNextNode(); + } + + public string GetCurrentDialogueLine() + { + if (currentNode == null || currentNode.dialogueLines.Count == 0) return string.Empty; + + if (currentLineIndex < 0 || currentLineIndex >= currentNode.dialogueLines.Count) + return string.Empty; + + return currentNode.dialogueLines[currentLineIndex]; + } + + // Methods to handle dialogue responses for item slots + public void HandleItemSlotInteraction(string slotID, string itemID, bool isForbiddenItem) + { + if (!IsActive || IsCompleted || currentNode == null || + currentNode.nodeType != RuntimeDialogueNodeType.WaitOnSlot) + return; + + // If this is the slot we're waiting for + if (currentNode.slotItemID == slotID) + { + // If correct item is slotted, move to next node + if (itemID == slotID) + { + MoveToNextNode(); + } + // If it's a forbidden item, show the forbidden dialogue + else if (isForbiddenItem && currentNode.forbiddenItemLines.Count > 0) + { + ShowResponseLines(currentNode.forbiddenItemLines, currentNode.loopThroughForbiddenLines); + } + // Otherwise show incorrect item dialogue + else if (currentNode.incorrectItemLines.Count > 0) + { + ShowResponseLines(currentNode.incorrectItemLines, currentNode.loopThroughIncorrectLines); + } + } + } + + private void ShowResponseLines(List lines, bool loopThrough) + { + StartCoroutine(ShowResponseRoutine(lines, loopThrough)); + } + + private IEnumerator ShowResponseRoutine(List lines, bool loopThrough) + { + // Store original node and line index + var originalNode = currentNode; + var originalLineIndex = currentLineIndex; + + // Show each response line + for (int i = 0; i < lines.Count; i++) + { + // Break if dialogue state has changed + if (currentNode != originalNode || !IsActive || IsCompleted) + break; + + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, lines[i]); + + // Wait for input to continue + yield return new WaitForSeconds(2f); // Wait time between lines, can be adjusted + + // If we should loop and we're at the end, start over + if (loopThrough && i == lines.Count - 1) + i = -1; + + // Break after first iteration if not looping + if (!loopThrough && i == 0) + break; + } + + // Restore original dialogue line + if (currentNode == originalNode && IsActive && !IsCompleted) + { + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + } + } + + private void MoveToNextNode() + { + // Unregister from events based on current node + UnregisterFromEvents(); + + // If there's no next node, complete the dialogue + if (string.IsNullOrEmpty(currentNode.nextNodeID)) + { + IsActive = false; + IsCompleted = true; + OnDialogueCompleted?.Invoke(); + return; + } + + // Move to the next node + currentNode = dialogueGraph.GetNodeByID(currentNode.nextNodeID); + currentLineIndex = 0; + + // Register for events based on new node + RegisterForEvents(); + + // Process the new node + ProcessCurrentNode(); + } + + private void ProcessCurrentNode() + { + if (currentNode == null) + { + Debug.LogError("DialogueComponent: Current node is null!"); + return; + } + + // Handle different node types + switch (currentNode.nodeType) + { + case RuntimeDialogueNodeType.Dialogue: + isWaitingForCondition = false; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + break; + + case RuntimeDialogueNodeType.WaitOnPuzzleStep: + HandlePuzzleStepNode(); + break; + + case RuntimeDialogueNodeType.WaitOnPickup: + HandlePickupNode(); + break; + + case RuntimeDialogueNodeType.WaitOnSlot: + HandleSlotNode(); + break; + + case RuntimeDialogueNodeType.End: + IsActive = false; + IsCompleted = true; + OnDialogueCompleted?.Invoke(); + break; + + default: + Debug.LogError($"DialogueComponent: Unknown node type {currentNode.nodeType}"); + break; + } + } + + private void HandlePuzzleStepNode() + { + if (puzzleManager == null) + { + Debug.LogError("DialogueComponent: PuzzleManager is required for WaitOnPuzzleStep nodes!"); + MoveToNextNode(); + return; + } + + // Check if the puzzle step is already completed + if (IsPuzzleStepComplete(currentNode.puzzleStepID)) + { + isWaitingForCondition = false; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + return; + } + + // Otherwise, wait for the puzzle step + isWaitingForCondition = true; + OnConditionStatusChanged?.Invoke(false); + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + } + + private void HandlePickupNode() + { + if (itemManager == null) + { + Debug.LogError("DialogueComponent: ItemManager is required for WaitOnPickup nodes!"); + MoveToNextNode(); + return; + } + + // Check if the item is already picked up + if (IsItemPickedUp(currentNode.pickupItemID)) + { + isWaitingForCondition = false; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + return; + } + + // Otherwise, wait for the item pickup + isWaitingForCondition = true; + OnConditionStatusChanged?.Invoke(false); + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + } + + private void HandleSlotNode() + { + if (itemManager == null) + { + Debug.LogError("DialogueComponent: ItemManager is required for WaitOnSlot nodes!"); + MoveToNextNode(); + return; + } + + // Check if the slot already has the correct item + if (IsItemSlotted(currentNode.slotItemID)) + { + isWaitingForCondition = false; + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + MoveToNextNode(); + return; + } + + // Otherwise, wait for the correct item to be slotted + isWaitingForCondition = true; + OnConditionStatusChanged?.Invoke(false); + OnDialogueLineChanged?.Invoke(CurrentSpeakerName, GetCurrentDialogueLine()); + } + + private void RegisterForEvents() + { + if (currentNode == null) return; + + switch (currentNode.nodeType) + { + case RuntimeDialogueNodeType.WaitOnPuzzleStep: + if (puzzleManager != null) + puzzleManager.OnStepCompleted += OnStepCompleted; + break; + + case RuntimeDialogueNodeType.WaitOnPickup: + if (itemManager != null) + itemManager.OnItemPickedUp += OnItemPickedUp; + break; + + case RuntimeDialogueNodeType.WaitOnSlot: + if (itemManager != null) + itemManager.OnCorrectItemSlotted += OnCorrectItemSlotted; + break; + } + } + + private void UnregisterFromEvents() + { + if (currentNode == null) return; + + switch (currentNode.nodeType) + { + case RuntimeDialogueNodeType.WaitOnPuzzleStep: + if (puzzleManager != null) + puzzleManager.OnStepCompleted -= OnStepCompleted; + break; + + case RuntimeDialogueNodeType.WaitOnPickup: + if (itemManager != null) + itemManager.OnItemPickedUp -= OnItemPickedUp; + break; + + case RuntimeDialogueNodeType.WaitOnSlot: + if (itemManager != null) + itemManager.OnCorrectItemSlotted -= OnCorrectItemSlotted; + break; + } + } + + // Event handlers for PuzzleManager + private void OnStepCompleted(PuzzleStepSO step) + { + if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPuzzleStep) + return; + + if (step.stepId == currentNode.puzzleStepID) + { + isWaitingForCondition = false; + OnConditionStatusChanged?.Invoke(true); + MoveToNextNode(); + } + } + + // Event handlers for ItemManager + private void OnItemPickedUp(PickupItemData item) + { + if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnPickup) + return; + + if (item.itemId == currentNode.pickupItemID) + { + isWaitingForCondition = false; + OnConditionStatusChanged?.Invoke(true); + MoveToNextNode(); + } + } + + private void OnCorrectItemSlotted(PickupItemData slotDefinition, PickupItemData slottedItem) + { + if (!IsActive || !isWaitingForCondition || currentNode.nodeType != RuntimeDialogueNodeType.WaitOnSlot) + return; + + if (slotDefinition.itemId == currentNode.slotItemID) + { + isWaitingForCondition = false; + OnConditionStatusChanged?.Invoke(true); + MoveToNextNode(); + } + } + + // Helper methods + private bool IsPuzzleStepComplete(string stepID) + { + if (puzzleManager == null) return false; + + // Use the public method instead of accessing the private field + return puzzleManager.IsPuzzleStepCompleted(stepID); + } + + private bool IsItemPickedUp(string itemID) + { + if (itemManager == null) return false; + + // Check if any picked up item has this ID + foreach (var pickup in itemManager.Pickups) + { + if (pickup.isPickedUp && pickup.itemData != null && pickup.itemData.itemId == itemID) + { + return true; + } + } + return false; + } + + private bool IsItemSlotted(string slotID) + { + if (itemManager == null) return false; + + // Check if any slot has the correct item with this ID + foreach (var slot in itemManager.ItemSlots) + { + if (slot.CurrentSlottedState == ItemSlotState.Correct && + slot.itemData != null && slot.itemData.itemId == slotID) + { + return true; + } + } + return false; + } + + // Editor functionality + public void SetDialogueGraph(RuntimeDialogueGraph graph) + { + dialogueGraph = graph; } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/Dialogue/DialogueComponent.cs.meta b/Assets/Scripts/Dialogue/DialogueComponent.cs.meta index 4255aa2e..8da2c943 100644 --- a/Assets/Scripts/Dialogue/DialogueComponent.cs.meta +++ b/Assets/Scripts/Dialogue/DialogueComponent.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 749c3dece1c14b82845c175203a2e7dc -timeCreated: 1758873871 \ No newline at end of file +guid: 25bbad45f1fa4183b30ad76c62256fd6 +timeCreated: 1758891211 \ No newline at end of file diff --git a/Assets/Scripts/Dialogue/RuntimeDialogueGraph.cs b/Assets/Scripts/Dialogue/RuntimeDialogueGraph.cs index 6ce0b79c..3b64e35a 100644 --- a/Assets/Scripts/Dialogue/RuntimeDialogueGraph.cs +++ b/Assets/Scripts/Dialogue/RuntimeDialogueGraph.cs @@ -4,19 +4,50 @@ using UnityEngine; namespace Dialogue { + [Serializable] + public enum RuntimeDialogueNodeType + { + Dialogue, + WaitOnPuzzleStep, + WaitOnPickup, + WaitOnSlot, + End + } + [Serializable] public class RuntimeDialogueGraph : ScriptableObject { public string entryNodeID; public string speakerName; public List allNodes = new List(); + + // Helper method to find a node by ID + public RuntimeDialogueNode GetNodeByID(string id) + { + return allNodes.Find(n => n.nodeID == id); + } } [Serializable] public class RuntimeDialogueNode { public string nodeID; - public string dialogueLine; + public RuntimeDialogueNodeType nodeType; public string nextNodeID; + + // Basic dialogue + public List dialogueLines = new List(); + public bool loopThroughLines; + + // Conditional nodes + public string puzzleStepID; // For WaitOnPuzzleStep + public string pickupItemID; // For WaitOnPickup + public string slotItemID; // For WaitOnSlot + + // For WaitOnSlot - different responses + public List incorrectItemLines = new List(); + public bool loopThroughIncorrectLines; + public List forbiddenItemLines = new List(); + public bool loopThroughForbiddenLines; } } \ No newline at end of file diff --git a/Assets/Scripts/Interactions/ItemSlot.cs b/Assets/Scripts/Interactions/ItemSlot.cs index 4c9abf57..1fcd69cf 100644 --- a/Assets/Scripts/Interactions/ItemSlot.cs +++ b/Assets/Scripts/Interactions/ItemSlot.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEngine.Events; using System; // for Action +using Core; // register with ItemManager namespace Interactions { @@ -174,5 +175,16 @@ namespace Interactions Interactable.BroadcastInteractionComplete(false); } } + + // Register with ItemManager when enabled + void Start() + { + ItemManager.Instance?.RegisterItemSlot(this); + } + + void OnDestroy() + { + ItemManager.Instance?.UnregisterItemSlot(this); + } } } diff --git a/Assets/Scripts/Interactions/Pickup.cs b/Assets/Scripts/Interactions/Pickup.cs index 9827523a..f9361889 100644 --- a/Assets/Scripts/Interactions/Pickup.cs +++ b/Assets/Scripts/Interactions/Pickup.cs @@ -1,6 +1,7 @@ using Input; using UnityEngine; using System; // added for Action +using Core; // register with ItemManager namespace Interactions { @@ -12,6 +13,9 @@ namespace Interactions protected Interactable Interactable; private PlayerTouchController _playerRef; protected FollowerController FollowerController; + + // Track if the item has been picked up + public bool isPickedUp { get; private set; } // Event: invoked when the item was picked up successfully public event Action OnItemPickedUp; @@ -34,6 +38,14 @@ namespace Interactions ApplyItemData(); } + /// + /// Register with ItemManager on Start + /// + void Start() + { + ItemManager.Instance?.RegisterPickup(this); + } + /// /// Unity OnDestroy callback. Cleans up event handlers. /// @@ -44,6 +56,9 @@ namespace Interactions Interactable.interactionStarted.RemoveListener(OnInteractionStarted); Interactable.characterArrived.RemoveListener(OnCharacterArrived); } + + // Unregister from ItemManager + ItemManager.Instance?.UnregisterPickup(this); } #if UNITY_EDITOR @@ -96,9 +111,10 @@ namespace Interactions bool wasPickedUp = (combinationResult == FollowerController.CombinationResult.NotApplicable); Interactable.BroadcastInteractionComplete(wasPickedUp); - // Invoke native C# event when the item was picked up successfully + // Update pickup state and invoke event when the item was picked up successfully if (wasPickedUp) { + isPickedUp = true; OnItemPickedUp?.Invoke(itemData); } } diff --git a/Assets/Scripts/Interactions/PickupItemData.cs b/Assets/Scripts/Interactions/PickupItemData.cs index 09d64a22..34931817 100644 --- a/Assets/Scripts/Interactions/PickupItemData.cs +++ b/Assets/Scripts/Interactions/PickupItemData.cs @@ -1,18 +1,58 @@ using UnityEngine; using System.Collections.Generic; +using System; [CreateAssetMenu(fileName = "PickupItemData", menuName = "Game/Pickup Item Data")] public class PickupItemData : ScriptableObject { + [SerializeField] private string _itemId; + public string itemName; [TextArea] public string description; public Sprite mapSprite; + + // Read-only property for itemId + public string itemId => _itemId; + + // Auto-generate ID on creation or validation + void OnValidate() + { + // Only generate if empty + if (string.IsNullOrEmpty(_itemId)) + { + _itemId = GenerateItemId(); + +#if UNITY_EDITOR + // Mark the asset as dirty to ensure the ID is saved + UnityEditor.EditorUtility.SetDirty(this); +#endif + } + } + + private string GenerateItemId() + { + // Use asset name as the basis for the ID to keep it somewhat readable + string baseName = name.Replace(" ", "").ToLowerInvariant(); + + // Add a unique suffix based on a GUID + string uniqueSuffix = Guid.NewGuid().ToString().Substring(0, 8); + + return $"{baseName}_{uniqueSuffix}"; + } + + // Method to manually regenerate ID if needed (for editor scripts) + public void RegenerateId() + { + _itemId = GenerateItemId(); + } public static bool AreEquivalent(PickupItemData a, PickupItemData b) { - if (ReferenceEquals(a, b)) return true; - if (a is null || b is null) return false; + // First compare by itemId if available + if (!string.IsNullOrEmpty(a.itemId) && !string.IsNullOrEmpty(b.itemId)) + return a.itemId == b.itemId; + // Compare by itemName as a fallback return a.itemName == b.itemName; } diff --git a/Assets/Scripts/PuzzleS/PuzzleManager.cs b/Assets/Scripts/PuzzleS/PuzzleManager.cs index 77f1cdb4..b73ec3d3 100644 --- a/Assets/Scripts/PuzzleS/PuzzleManager.cs +++ b/Assets/Scripts/PuzzleS/PuzzleManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; @@ -200,6 +201,16 @@ namespace PuzzleS return _unlockedSteps.Contains(step); } + /// + /// Checks if a puzzle step with the specified ID has been completed + /// + /// The ID of the puzzle step to check + /// True if the step has been completed, false otherwise + public bool IsPuzzleStepCompleted(string stepId) + { + return _completedSteps.Any(step => step.stepId == stepId); + } + void OnApplicationQuit() { _isQuitting = true;